From 9e1b5784b87d843870ced9b213a1b387ef75b9de Mon Sep 17 00:00:00 2001 From: wehub-resource-sync Date: Tue, 14 Jul 2026 10:24:00 +0800 Subject: [PATCH] chore: import upstream snapshot with attribution --- .github/workflows/test.yml | 17 + .gitignore | 35 + .../yao-meta-skill-2-comparison/index.html | 1336 + .../yao-meta-skill-1-vs-2-en.png | Bin 0 -> 789579 bytes .../yao-meta-skill-1-vs-2-mobile.png | Bin 0 -> 701050 bytes .../yao-meta-skill-1-vs-2.png | Bin 0 -> 1157263 bytes .../geo-content-brief-skill/README.md | 72 + .../geo-content-brief-skill/SKILL.md | 57 + .../agents/interface.yaml | 29 + .../evals/output_cases.jsonl | 2 + .../evals/trigger_cases.jsonl | 4 + .../geo-content-brief-skill/manifest.json | 24 + .../references/brief-structure.md | 23 + .../references/review-checklist.md | 11 + .../reports/adoption_drift_report.json | 62 + .../reports/adoption_drift_report.md | 36 + .../reports/artifact-design-profile.json | 115 + .../reports/artifact-design-profile.md | 93 + .../reports/compiled_targets.json | 2229 ++ .../reports/compiled_targets.md | 73 + .../reports/intent-confidence.json | 83 + .../reports/intent-confidence.md | 32 + .../reports/intent-context.json | 11 + .../reports/intent-dialogue.json | 79 + .../reports/intent-dialogue.md | 75 + .../reports/iteration-directions.json | 52 + .../reports/iteration-directions.md | 62 + .../reports/output-risk-profile.json | 147 + .../reports/output-risk-profile.md | 60 + .../reports/prompt-quality-profile.json | 146 + .../reports/prompt-quality-profile.md | 94 + .../reports/reference-scan.json | 36 + .../reports/reference-scan.md | 54 + .../reports/reference-synthesis.json | 171 + .../reports/reference-synthesis.md | 81 + .../reports/review-studio.html | 720 + .../reports/review-studio.json | 4450 +++ .../reports/review-viewer.html | 376 + .../reports/review-viewer.json | 1921 ++ .../reports/review_annotations.json | 24 + .../reports/review_annotations.md | 19 + .../reports/review_waivers.json | 44 + .../reports/review_waivers.md | 34 + .../reports/skill-ir.json | 93 + .../reports/skill-overview.html | 986 + .../reports/skill-overview.json | 1060 + .../reports/system-model.json | 166 + .../reports/system-model.md | 156 + AGENTS.md | 149 + LICENSE | 21 + Makefile | 283 + README.md | 729 + README.wehub.md | 7 + SKILL.md | 53 + VERSION | 1 + agents/interface.yaml | 26 + assets/review-studio.css | 749 + assets/review-viewer.css | 176 + assets/skill-overview.css | 735 + assets/skill-overview.js | 37 + docs/README.fr-FR.md | 240 + docs/README.ja-JP.md | 240 + docs/README.ru-RU.md | 240 + docs/README.zh-CN.md | 333 + docs/assets/social-preview.png | Bin 0 -> 48266 bytes docs/assets/social-preview.svg | 33 + docs/migration-v2.md | 63 + evals/README.md | 86 + evals/adaptation/user_signals.example.jsonl | 8 + evals/adversarial/trigger_cases.json | 33 + evals/baseline_description.txt | 1 + evals/blind_holdout/trigger_cases.json | 42 + evals/confusion/route_cases.json | 90 + evals/dev/trigger_cases.json | 106 + evals/failure-cases.md | 44 + .../2026-03-31-context-first-pack.json | 27 + .../2026-03-31-family-suite-expansion.json | 26 + .../2026-03-31-governance-quality-gates.json | 27 + ...026-03-31-governed-assets-and-history.json | 27 + ...2026-04-01-route-scorecard-foundation.json | 30 + evals/history/README.md | 16 + ...-03-31-description-optimization-suite.json | 45 + ...ersarial-calibration-and-family-drift.json | 342 + ...04-01-blind-holdout-and-drift-history.json | 78 + .../2026-04-01-judge-backed-blind-eval.json | 444 + .../description_optimization/README.md | 18 + evals/holdout/trigger_cases.json | 70 + evals/improved_description.txt | 1 + evals/output/cases.jsonl | 5 + evals/output/fixtures/release-brief-source.md | 14 + evals/output/schema.json | 85 + evals/packaging_expectations.json | 35 + evals/promotion_policy.md | 92 + evals/semantic_config.json | 490 + evals/train/trigger_cases.json | 142 + evals/trigger_cases.json | 390 + evidence/world_class/README.md | 45 + evidence/world_class/intake.schema.json | 440 + .../templates/human-adjudication.intake.json | 64 + .../native-client-telemetry.intake.json | 53 + .../native-permission-enforcement.intake.json | 61 + .../templates/provider-holdout.intake.json | 50 + examples/README.md | 41 + .../design-summary.md | 9 + .../generated-skill/SKILL.md | 68 + .../generated-skill/agents/interface.yaml | 4 + .../generated-skill/evals/trigger_cases.json | 16 + .../input/release_input_example.json | 31 + .../generated-skill/manifest.json | 21 + .../outputs/release_packet_example.md | 29 + .../references/migration-template.md | 22 + .../references/release-checklist.md | 31 + .../generated-skill/references/risk-matrix.md | 14 + .../references/stakeholder-comms.md | 21 + .../reports/context_budget.json | 54 + .../scripts/build_release_packet.py | 63 + .../raw-workflow.md | 27 + examples/evolution-frontend-review/README.md | 9 + .../evolution-frontend-review/eval-delta.md | 19 + .../evolution-frontend-review/final/SKILL.md | 22 + .../evolution-frontend-review/raw-workflow.md | 10 + .../evolution-frontend-review/v0/SKILL.md | 11 + .../evolution-frontend-review/v1/SKILL.md | 14 + .../design-summary.md | 30 + .../generated-skill/SKILL.md | 70 + .../generated-skill/agents/interface.yaml | 10 + .../generated-skill/evals/trigger_cases.json | 14 + .../input/incident_input_example.json | 24 + .../generated-skill/manifest.json | 26 + .../outputs/incident_packet_example.md | 27 + .../references/comms-policy.md | 9 + .../references/review-policy.md | 14 + .../references/severity-matrix.md | 7 + .../reports/context_budget.json | 54 + .../reports/governance_score.json | 25 + .../reports/revision_history.md | 3 + .../scripts/build_incident_packet.py | 47 + .../adversarial/trigger_cases.json | 33 + .../optimization/baseline_description.txt | 1 + .../blind_holdout/trigger_cases.json | 33 + .../optimization/dev/trigger_cases.json | 41 + .../optimization/holdout/trigger_cases.json | 33 + .../reports/description_optimization.json | 6261 ++++ .../reports/description_optimization.md | 64 + .../optimization/semantic_config.json | 160 + .../governed-incident-command/raw-workflow.md | 18 + .../simple-note-cleanup/design-summary.md | 7 + .../generated-skill/SKILL.md | 13 + .../generated-skill/agents/interface.yaml | 4 + examples/simple-note-cleanup/raw-workflow.md | 14 + .../team-frontend-review/design-summary.md | 7 + .../generated-skill/SKILL.md | 26 + .../generated-skill/agents/interface.yaml | 4 + .../generated-skill/references/checklist.md | 13 + .../adversarial/trigger_cases.json | 33 + .../optimization/baseline_description.txt | 1 + .../blind_holdout/trigger_cases.json | 33 + .../optimization/dev/trigger_cases.json | 53 + .../optimization/holdout/trigger_cases.json | 33 + .../reports/description_optimization.json | 6798 ++++ .../reports/description_optimization.md | 64 + .../optimization/semantic_config.json | 156 + examples/team-frontend-review/raw-workflow.md | 14 + failures/README.md | 18 + .../document-export-vs-agent-skill/README.md | 28 + .../document-export-vs-agent-skill/cases.json | 24 + failures/explain-not-package/README.md | 28 + failures/explain-not-package/cases.json | 24 + failures/future-outline-vs-build/README.md | 28 + failures/future-outline-vs-build/cases.json | 19 + failures/one-off-vs-reusable/README.md | 28 + failures/one-off-vs-reusable/cases.json | 19 + manifest.json | 24 + references/artifact-design-doctrine.md | 49 + references/authoring-discipline.md | 78 + references/autonomous-adaptation.md | 65 + references/distribution-registry-method.md | 60 + references/eval-playbook.md | 69 + references/gate-selection.md | 68 + references/governance.md | 134 + references/human-review-template.md | 54 + references/intent-dialogue.md | 138 + references/iteration-philosophy.md | 30 + references/non-skill-decision-tree.md | 39 + references/operating-modes.md | 107 + references/output-eval-method.md | 113 + references/output-quality-risk.md | 41 + references/output-visual-quality.md | 53 + references/packaging-contracts.md | 70 + references/pattern-extraction-doctrine.md | 76 + references/platform-capability-matrix.md | 49 + references/prompt-engineering-doctrine.md | 76 + references/qa-ladder.md | 57 + references/reference-scan.md | 126 + references/regression-cause-taxonomy.md | 80 + references/resource-boundaries.md | 120 + references/review-studio-method.md | 87 + references/review-waiver-method.md | 76 + references/runtime-conformance-method.md | 21 + references/skill-archetypes.md | 86 + references/skill-atlas-method.md | 35 + references/skill-engineering-method.md | 210 + references/skill-ir-method.md | 41 + references/skillops-decision-policy.md | 53 + references/systems-thinking-doctrine.md | 75 + references/telemetry-drift-method.md | 182 + references/trust-security-method.md | 79 + references/user-memory-policy.md | 35 + registry/examples/yao-meta-skill-1.0.0.json | 43 + registry/index.json | 22 + registry/index.schema.json | 30 + registry/package.schema.json | 53 + registry/packages/yao-meta-skill.json | 57 + reports/adaptation_approval_ledger.json | 63 + reports/adaptation_proposals.json | 248 + reports/adaptation_proposals.md | 119 + reports/adaptation_regression_report.json | 87 + reports/adaptation_regression_report.md | 11 + reports/adoption_drift_report.json | 84 + reports/adoption_drift_report.md | 36 + reports/architecture_maintainability.json | 181 + reports/architecture_maintainability.md | 66 + reports/artifact-design-profile.json | 120 + reports/artifact-design-profile.md | 93 + reports/baseline-compare.json | 111 + reports/baseline-compare.md | 18 + reports/benchmark_methodology.md | 67 + reports/benchmark_reproducibility.json | 493 + reports/benchmark_reproducibility.md | 182 + .../DO_NOT_OPEN_answer_key.html | 149 + .../DO_NOT_OPEN_answer_key.json | 62 + .../blind-human-review-2026-06-29/README.md | 15 + .../adjudication.md | 39 + .../blind-pack.json | 98 + .../case-01-support-triage.html | 380 + ...ase-02-monthly-revenue-reconciliation.html | 368 + .../case-03-webinar-repurposing.html | 356 + .../case-04-incident-postmortem.html | 361 + .../case-05-pr-review-followup.html | 344 + .../generate_pack.py | 1245 + .../blind-human-review-2026-06-29/index.html | 220 + .../review-decisions-template.json | 42 + .../review-decisions.recorded.json | 55 + reports/candidate_registry.json | 789 + reports/candidate_registry.md | 23 + reports/compiled_targets.json | 4769 +++ reports/compiled_targets.md | 73 + reports/conformance_matrix.json | 239 + reports/conformance_matrix.md | 19 + reports/context_budget.json | 74 + reports/context_budget.md | 9 + reports/context_budget_summary.json | 93 + reports/description_drift_history.md | 33 + reports/description_optimization.json | 10520 ++++++ reports/description_optimization.md | 65 + reports/description_optimization_suite.json | 437 + reports/description_optimization_suite.md | 15 + reports/eval_suite.json | 3990 +++ reports/evidence_consistency.json | 3609 ++ reports/evidence_consistency.md | 57 + reports/family_summary.md | 34 + reports/github-benchmark-scan.json | 188 + reports/github-benchmark-scan.md | 166 + reports/governance_score.json | 25 + reports/install_simulation.json | 286 + reports/install_simulation.md | 77 + reports/intent-confidence.json | 53 + reports/intent-confidence.md | 26 + reports/intent-context.json | 37 + reports/intent-dialogue.json | 79 + reports/intent-dialogue.md | 75 + reports/iteration-directions.json | 52 + reports/iteration-directions.md | 62 + .../governed-incident-command/bundle.json | 123 + .../governed-incident-command/bundle.md | 53 + .../governed-incident-command/review.md | 8 + .../team-frontend-review/bundle.json | 119 + .../team-frontend-review/bundle.md | 51 + .../team-frontend-review/review.md | 8 + .../yao-meta-skill/bundle.json | 135 + .../yao-meta-skill/bundle.md | 51 + .../yao-meta-skill/review.md | 8 + reports/iteration_ledger.md | 49 + reports/output-risk-profile.json | 143 + reports/output-risk-profile.md | 60 + reports/output_blind_answer_key.json | 56 + reports/output_blind_review_pack.json | 204 + reports/output_blind_review_pack.md | 97 + reports/output_execution_runs.json | 312 + reports/output_execution_runs.md | 37 + reports/output_quality_scorecard.json | 816 + reports/output_quality_scorecard.md | 34 + reports/output_review_adjudication.json | 278 + reports/output_review_adjudication.md | 207 + reports/output_review_decisions.json | 67 + reports/output_review_kit.html | 220 + reports/output_review_kit.json | 271 + reports/output_review_kit.md | 141 + reports/package_verification.json | 672 + reports/package_verification.md | 152 + reports/portability_score.json | 27 + reports/portability_score.md | 23 + reports/promotion_decisions.json | 795 + reports/promotion_decisions.md | 7 + reports/prompt-quality-profile.json | 135 + reports/prompt-quality-profile.md | 94 + reports/python_compatibility.json | 1441 + reports/python_compatibility.md | 20 + reports/reference-scan.json | 40 + reports/reference-scan.md | 55 + reports/reference-synthesis.json | 265 + reports/reference-synthesis.md | 105 + reports/registry_audit.json | 93 + reports/registry_audit.md | 37 + reports/regression_history.md | 9 + reports/review-studio.html | 952 + reports/review-studio.json | 28783 ++++++++++++++++ reports/review-viewer.html | 376 + reports/review-viewer.json | 14811 ++++++++ reports/review_annotations.json | 24 + reports/review_annotations.md | 19 + reports/review_waivers.json | 120 + reports/review_waivers.md | 72 + reports/route_scorecard.json | 593 + reports/route_scorecard.md | 30 + reports/runtime_permission_probes.json | 391 + reports/runtime_permission_probes.md | 40 + reports/security_trust_report.json | 3588 ++ reports/security_trust_report.md | 219 + reports/skill-interpretation.html | 1005 + reports/skill-interpretation.json | 2296 ++ reports/skill-os-2-review.md | 165 + reports/skill-overview-v2-plan.md | 489 + reports/skill-overview.html | 1005 + reports/skill-overview.json | 2266 ++ reports/skill_atlas.html | 57 + reports/skill_atlas.json | 1033 + reports/skill_os2_audit.json | 438 + reports/skill_os2_audit.md | 101 + reports/skill_os2_coverage.json | 811 + reports/skill_os2_coverage.md | 246 + reports/skillops/daily/2026-06-16.json | 513 + reports/skillops/daily/2026-06-16.md | 174 + reports/skillops/daily/2026-06-17.json | 513 + reports/skillops/daily/2026-06-17.md | 174 + reports/skillops/daily/2026-06-20.json | 513 + reports/skillops/daily/2026-06-20.md | 174 + reports/skillops/daily/2026-06-21.json | 513 + reports/skillops/daily/2026-06-21.md | 174 + reports/skillops/daily/2026-07-02.json | 513 + reports/skillops/daily/2026-07-02.md | 174 + reports/skillops/weekly/2026-W25.json | 322 + reports/skillops/weekly/2026-W25.md | 110 + reports/skillops/weekly/2026-W27.json | 323 + reports/skillops/weekly/2026-W27.md | 110 + reports/system-model.json | 181 + reports/system-model.md | 171 + reports/telemetry_hook_recipes.json | 338 + reports/telemetry_hook_recipes.md | 35 + reports/upgrade_check.json | 96 + reports/upgrade_check.md | 31 + reports/user_patterns.json | 136 + reports/user_patterns.md | 72 + reports/world_class_claim_guard.json | 859 + reports/world_class_claim_guard.md | 33 + reports/world_class_evidence_intake.json | 398 + reports/world_class_evidence_intake.md | 265 + reports/world_class_evidence_ledger.json | 602 + reports/world_class_evidence_ledger.md | 226 + reports/world_class_evidence_plan.json | 372 + reports/world_class_evidence_plan.md | 190 + reports/world_class_evidence_preflight.html | 975 + reports/world_class_evidence_preflight.json | 1978 ++ reports/world_class_evidence_preflight.md | 200 + reports/world_class_operator_runbook.html | 168 + reports/world_class_operator_runbook.json | 1691 + reports/world_class_operator_runbook.md | 407 + reports/world_class_submission_review.json | 442 + reports/world_class_submission_review.md | 178 + requirements-ci.txt | 1 + runtime/conformance/README.md | 19 + runtime/conformance/schema.json | 56 + schemas/adaptation-proposal.schema.json | 226 + schemas/skill-interpretation.schema.json | 115 + schemas/skillops-opportunity.schema.json | 112 + scripts/adaptation_patch_safety.py | 76 + scripts/adaptation_report_contracts.py | 111 + scripts/adjudicate_output_review.py | 566 + scripts/apply_adaptation.py | 543 + scripts/benchmark_release_lock.py | 98 + scripts/build_confusion_matrix.py | 249 + scripts/build_skill_atlas.py | 586 + scripts/build_skill_atlas_layout.py | 103 + scripts/build_skill_atlas_opportunities.py | 82 + scripts/check_update.py | 211 + scripts/ci_test.py | 163 + scripts/collect_feedback.py | 128 + scripts/compile_skill.py | 447 + scripts/compile_skill_targets.py | 282 + scripts/context_sizer.py | 160 + scripts/create_iteration_snapshot.py | 195 + scripts/cross_packager.py | 495 + scripts/cross_packager_contracts.py | 107 + scripts/description_optimizer_reporting.py | 141 + scripts/diff_eval.py | 25 + scripts/emit_telemetry_event.py | 128 + .../evidence_consistency_artifact_roles.py | 201 + scripts/evidence_consistency_core.py | 328 + scripts/evidence_consistency_phase_queue.py | 173 + scripts/evidence_consistency_release.py | 132 + .../evidence_consistency_skill_os2_review.py | 83 + scripts/evidence_consistency_world_class.py | 222 + scripts/export_skill_ir.py | 360 + scripts/github_benchmark_scan.py | 431 + scripts/governance_check.py | 203 + scripts/html_rendering.py | 14 + scripts/import_output_review_decisions.py | 329 + scripts/import_telemetry_events.py | 139 + scripts/init_skill.py | 486 + scripts/judge_blind_eval.py | 271 + scripts/lint_skill.py | 40 + scripts/local_output_eval_runner.py | 65 + scripts/optimize_description.py | 585 + scripts/output_review_privacy.py | 81 + scripts/prepare_output_review_kit.py | 485 + scripts/prepare_world_class_submission_kit.py | 560 + scripts/probe_runtime_permissions.py | 454 + scripts/promotion_checker.py | 417 + scripts/propose_adaptation.py | 305 + scripts/provider_output_eval_runner.py | 345 + scripts/python_compat_check.py | 351 + scripts/reference_synthesis_markdown.py | 99 + scripts/registry_audit.py | 370 + scripts/render_adoption_drift_report.py | 472 + .../render_architecture_maintainability.py | 280 + scripts/render_artifact_design_profile.py | 346 + scripts/render_baseline_compare.py | 131 + scripts/render_benchmark_reproducibility.py | 685 + scripts/render_context_reports.py | 98 + scripts/render_daily_skillops_report.py | 484 + scripts/render_description_drift_history.py | 95 + scripts/render_eval_dashboard.py | 126 + scripts/render_evidence_consistency.py | 676 + scripts/render_intent_confidence.py | 379 + scripts/render_intent_dialogue.py | 279 + scripts/render_iteration_directions.py | 289 + scripts/render_iteration_ledger.py | 116 + scripts/render_output_risk_profile.py | 322 + scripts/render_portability_report.py | 150 + scripts/render_prompt_quality_profile.py | 361 + scripts/render_reference_scan.py | 299 + scripts/render_reference_synthesis.py | 552 + scripts/render_regression_history.py | 49 + scripts/render_review_annotations.py | 402 + scripts/render_review_studio.py | 578 + scripts/render_review_viewer.py | 517 + scripts/render_review_waivers.py | 395 + scripts/render_skill_interpretation.py | 108 + scripts/render_skill_os2_audit.py | 433 + scripts/render_skill_os2_coverage.py | 555 + scripts/render_skill_overview.py | 588 + scripts/render_social_preview.py | 186 + scripts/render_system_model.py | 481 + scripts/render_telemetry_hook_recipes.py | 258 + scripts/render_weekly_curator_report.py | 433 + scripts/render_world_class_claim_guard.py | 269 + scripts/render_world_class_evidence_intake.py | 354 + scripts/render_world_class_evidence_ledger.py | 389 + scripts/render_world_class_evidence_plan.py | 327 + .../render_world_class_operator_runbook.py | 651 + scripts/render_world_class_preflight.py | 471 + .../render_world_class_submission_review.py | 305 + scripts/resource_boundary_check.py | 405 + scripts/review_studio_action_evidence.py | 388 + scripts/review_studio_actions.py | 374 + scripts/review_studio_data.py | 387 + scripts/review_studio_formatting.py | 204 + scripts/review_studio_gate_contract.py | 82 + scripts/review_studio_gate_helpers.py | 140 + scripts/review_studio_gates.py | 543 + scripts/review_studio_layout.py | 49 + scripts/review_studio_output_review.py | 84 + scripts/review_studio_panels.py | 88 + scripts/review_studio_skillops.py | 48 + scripts/review_studio_waivers.py | 47 + scripts/review_studio_world_class.py | 143 + scripts/review_viewer_data.py | 316 + scripts/run_conformance_suite.py | 267 + scripts/run_description_optimization_suite.py | 344 + scripts/run_eval_suite.py | 89 + scripts/run_output_eval.py | 420 + scripts/run_output_execution.py | 382 + scripts/simulate_install.py | 392 + scripts/skill_ir_paths.py | 76 + scripts/skill_os2_coverage_markdown.py | 101 + scripts/skill_report_charts.py | 222 + scripts/skill_report_i18n.py | 386 + scripts/skill_report_layout.py | 49 + scripts/skill_report_metrics.py | 248 + scripts/skill_report_model.py | 554 + scripts/skill_report_sections.py | 114 + scripts/skill_report_sources.py | 154 + scripts/skill_report_world_class.py | 128 + scripts/skillops_opportunity.py | 214 + scripts/summarize_user_signals.py | 321 + scripts/sync_local_install.py | 279 + scripts/telemetry_native_host.py | 229 + scripts/trigger_eval.py | 306 + scripts/trust_check.py | 582 + scripts/trust_check_scripts.py | 146 + scripts/upgrade_check.py | 268 + scripts/validate_skill.py | 94 + scripts/verify_package.py | 282 + scripts/world_class_evidence_contract.py | 553 + scripts/world_class_human_evidence.py | 317 + .../world_class_native_permission_evidence.py | 157 + .../world_class_native_telemetry_evidence.py | 144 + ...rld_class_operator_runbook_coordination.py | 205 + scripts/world_class_phase_queue.py | 76 + scripts/world_class_preflight_layout.py | 350 + scripts/world_class_preflight_markdown.py | 175 + scripts/world_class_provider_evidence.py | 145 + scripts/world_class_repair_checklist.py | 286 + scripts/world_class_source_checks.py | 99 + .../world_class_submission_kit_rendering.py | 576 + scripts/world_class_submission_matrix.py | 161 + scripts/yao.py | 563 + scripts/yao_cli_adaptation_commands.py | 121 + scripts/yao_cli_config.py | 270 + scripts/yao_cli_create_commands.py | 338 + scripts/yao_cli_distribution_commands.py | 164 + scripts/yao_cli_operator_commands.py | 424 + scripts/yao_cli_output_commands.py | 134 + scripts/yao_cli_parser.py | 508 + scripts/yao_cli_parser_evidence.py | 135 + scripts/yao_cli_parser_operations.py | 268 + scripts/yao_cli_parser_operator.py | 58 + scripts/yao_cli_report_commands.py | 339 + scripts/yao_cli_runtime.py | 75 + scripts/yao_cli_telemetry.py | 95 + security/dependency_policy.md | 10 + security/network_policy.json | 53 + security/network_policy.md | 12 + security/permission_policy.json | 58 + security/permission_policy.md | 33 + security/script_policy.md | 13 + security/secret_policy.md | 7 + security/trust_policy.md | 21 + skill-ir/README.md | 24 + skill-ir/examples/yao-meta-skill.json | 387 + skill-ir/schema.json | 156 + skill_atlas/README.md | 20 + skill_atlas/catalog.json | 709 + skill_atlas/catalog.schema.json | 33 + skill_atlas/dependency_graph.json | 49 + skill_atlas/drift_signals.json | 1 + skill_atlas/no_route_opportunities.json | 1 + skill_atlas/owner_review_gaps.json | 89 + skill_atlas/policy.json | 23 + skill_atlas/route_overlap_matrix.csv | 56 + skill_atlas/stale_skills.json | 67 + templates/basic_skill.md.j2 | 42 + templates/complex_skill.md.j2 | 61 + .../github_benchmark_scan/bundle.json | 39 + .../governance_invalid_manifest/SKILL.md | 8 + .../governance_invalid_manifest/manifest.json | 8 + tests/fixtures/package_invalid_yaml/SKILL.md | 6 + .../agents/interface.yaml | 8 + .../package_missing_interface_field/SKILL.md | 6 + .../agents/interface.yaml | 19 + tests/review_studio_helpers.py | 194 + tests/review_studio_world_class_assertions.py | 148 + tests/snapshots/claude_adapter.json | 26 + tests/snapshots/generic_adapter.json | 26 + tests/snapshots/openai_adapter.json | 26 + tests/snapshots/vscode_adapter.json | 26 + tests/verify_adaptation_safety.py | 460 + tests/verify_adapter_snapshots.py | 169 + tests/verify_adoption_drift.py | 205 + tests/verify_architecture_maintainability.py | 121 + tests/verify_artifact_design_profile.py | 62 + tests/verify_baseline_compare.py | 46 + tests/verify_benchmark_reproducibility.py | 314 + tests/verify_compile_skill.py | 107 + tests/verify_conformance_suite.py | 106 + tests/verify_daily_skillops.py | 249 + tests/verify_description_optimization.py | 138 + tests/verify_evidence_consistency.py | 719 + tests/verify_failure_regressions.py | 66 + tests/verify_feedback.py | 62 + tests/verify_github_benchmark_scan.py | 92 + tests/verify_html_rendering.py | 23 + tests/verify_install_simulation.py | 177 + tests/verify_intent_confidence.py | 65 + tests/verify_iteration_directions.py | 49 + tests/verify_local_install_sync.py | 193 + tests/verify_operator_ux.py | 198 + tests/verify_output_eval_lab.py | 108 + tests/verify_output_execution_runs.py | 508 + tests/verify_output_review_adjudication.py | 599 + tests/verify_output_review_kit.py | 223 + tests/verify_output_risk_profile.py | 60 + tests/verify_package_verification.py | 143 + tests/verify_packager_failures.py | 91 + tests/verify_portability_report.py | 67 + tests/verify_promotion_checker.py | 56 + tests/verify_prompt_quality_profile.py | 79 + tests/verify_python_compat_check.py | 96 + tests/verify_quality_checks.py | 276 + tests/verify_reference_scan.py | 79 + tests/verify_reference_synthesis.py | 82 + tests/verify_registry_audit.py | 107 + tests/verify_review_annotations.py | 197 + tests/verify_review_studio.py | 557 + tests/verify_review_viewer.py | 84 + tests/verify_review_waivers.py | 223 + tests/verify_route_confusion.py | 38 + tests/verify_runtime_permission_probes.py | 137 + tests/verify_security_boundaries.py | 225 + tests/verify_skill_atlas.py | 234 + tests/verify_skill_interpretation.py | 86 + tests/verify_skill_ir.py | 78 + tests/verify_skill_ir_paths.py | 66 + tests/verify_skill_os2_audit.py | 72 + tests/verify_skill_os2_coverage.py | 189 + tests/verify_skill_overview.py | 392 + tests/verify_skill_report_charts.py | 69 + tests/verify_skill_report_metrics.py | 87 + tests/verify_skillops_opportunity.py | 96 + tests/verify_system_model.py | 96 + tests/verify_telemetry_emit.py | 172 + tests/verify_telemetry_hooks.py | 153 + tests/verify_telemetry_import.py | 184 + tests/verify_telemetry_native_host.py | 218 + tests/verify_trust_check.py | 346 + tests/verify_upgrade_check.py | 93 + tests/verify_weekly_curator.py | 201 + tests/verify_world_class_claim_guard.py | 161 + tests/verify_world_class_evidence_intake.py | 703 + tests/verify_world_class_evidence_ledger.py | 587 + tests/verify_world_class_evidence_plan.py | 102 + tests/verify_world_class_operator_runbook.py | 313 + tests/verify_world_class_preflight.py | 355 + tests/verify_world_class_submission_kit.py | 351 + tests/verify_world_class_submission_review.py | 215 + tests/verify_yao_cli.py | 700 + tests/verify_yao_cli_world_class.py | 183 + tests/world_class_human_fixtures.py | 364 + .../world_class_native_telemetry_fixtures.py | 221 + tests/yao_cli_helpers.py | 140 + tests/yao_cli_report_refresh.py | 16 + 651 files changed, 215449 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 .previews/yao-meta-skill-2-comparison/index.html create mode 100644 .previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2-en.png create mode 100644 .previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2-mobile.png create mode 100644 .previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2.png create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/README.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/SKILL.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/agents/interface.yaml create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/output_cases.jsonl create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/trigger_cases.jsonl create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/manifest.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/brief-structure.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/review-checklist.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-context.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.html create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.html create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.md create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-ir.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.html create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.json create mode 100644 .previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.md create mode 100644 AGENTS.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 README.wehub.md create mode 100644 SKILL.md create mode 100644 VERSION create mode 100644 agents/interface.yaml create mode 100644 assets/review-studio.css create mode 100644 assets/review-viewer.css create mode 100644 assets/skill-overview.css create mode 100644 assets/skill-overview.js create mode 100644 docs/README.fr-FR.md create mode 100644 docs/README.ja-JP.md create mode 100644 docs/README.ru-RU.md create mode 100644 docs/README.zh-CN.md create mode 100644 docs/assets/social-preview.png create mode 100644 docs/assets/social-preview.svg create mode 100644 docs/migration-v2.md create mode 100644 evals/README.md create mode 100644 evals/adaptation/user_signals.example.jsonl create mode 100644 evals/adversarial/trigger_cases.json create mode 100644 evals/baseline_description.txt create mode 100644 evals/blind_holdout/trigger_cases.json create mode 100644 evals/confusion/route_cases.json create mode 100644 evals/dev/trigger_cases.json create mode 100644 evals/failure-cases.md create mode 100644 evals/history/2026-03-31-context-first-pack.json create mode 100644 evals/history/2026-03-31-family-suite-expansion.json create mode 100644 evals/history/2026-03-31-governance-quality-gates.json create mode 100644 evals/history/2026-03-31-governed-assets-and-history.json create mode 100644 evals/history/2026-04-01-route-scorecard-foundation.json create mode 100644 evals/history/README.md create mode 100644 evals/history/description_optimization/2026-03-31-description-optimization-suite.json create mode 100644 evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json create mode 100644 evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json create mode 100644 evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json create mode 100644 evals/history/description_optimization/README.md create mode 100644 evals/holdout/trigger_cases.json create mode 100644 evals/improved_description.txt create mode 100644 evals/output/cases.jsonl create mode 100644 evals/output/fixtures/release-brief-source.md create mode 100644 evals/output/schema.json create mode 100644 evals/packaging_expectations.json create mode 100644 evals/promotion_policy.md create mode 100644 evals/semantic_config.json create mode 100644 evals/train/trigger_cases.json create mode 100644 evals/trigger_cases.json create mode 100644 evidence/world_class/README.md create mode 100644 evidence/world_class/intake.schema.json create mode 100644 evidence/world_class/templates/human-adjudication.intake.json create mode 100644 evidence/world_class/templates/native-client-telemetry.intake.json create mode 100644 evidence/world_class/templates/native-permission-enforcement.intake.json create mode 100644 evidence/world_class/templates/provider-holdout.intake.json create mode 100644 examples/README.md create mode 100644 examples/complex-release-orchestrator/design-summary.md create mode 100644 examples/complex-release-orchestrator/generated-skill/SKILL.md create mode 100644 examples/complex-release-orchestrator/generated-skill/agents/interface.yaml create mode 100644 examples/complex-release-orchestrator/generated-skill/evals/trigger_cases.json create mode 100644 examples/complex-release-orchestrator/generated-skill/input/release_input_example.json create mode 100644 examples/complex-release-orchestrator/generated-skill/manifest.json create mode 100644 examples/complex-release-orchestrator/generated-skill/outputs/release_packet_example.md create mode 100644 examples/complex-release-orchestrator/generated-skill/references/migration-template.md create mode 100644 examples/complex-release-orchestrator/generated-skill/references/release-checklist.md create mode 100644 examples/complex-release-orchestrator/generated-skill/references/risk-matrix.md create mode 100644 examples/complex-release-orchestrator/generated-skill/references/stakeholder-comms.md create mode 100644 examples/complex-release-orchestrator/generated-skill/reports/context_budget.json create mode 100644 examples/complex-release-orchestrator/generated-skill/scripts/build_release_packet.py create mode 100644 examples/complex-release-orchestrator/raw-workflow.md create mode 100644 examples/evolution-frontend-review/README.md create mode 100644 examples/evolution-frontend-review/eval-delta.md create mode 100644 examples/evolution-frontend-review/final/SKILL.md create mode 100644 examples/evolution-frontend-review/raw-workflow.md create mode 100644 examples/evolution-frontend-review/v0/SKILL.md create mode 100644 examples/evolution-frontend-review/v1/SKILL.md create mode 100644 examples/governed-incident-command/design-summary.md create mode 100644 examples/governed-incident-command/generated-skill/SKILL.md create mode 100644 examples/governed-incident-command/generated-skill/agents/interface.yaml create mode 100644 examples/governed-incident-command/generated-skill/evals/trigger_cases.json create mode 100644 examples/governed-incident-command/generated-skill/input/incident_input_example.json create mode 100644 examples/governed-incident-command/generated-skill/manifest.json create mode 100644 examples/governed-incident-command/generated-skill/outputs/incident_packet_example.md create mode 100644 examples/governed-incident-command/generated-skill/references/comms-policy.md create mode 100644 examples/governed-incident-command/generated-skill/references/review-policy.md create mode 100644 examples/governed-incident-command/generated-skill/references/severity-matrix.md create mode 100644 examples/governed-incident-command/generated-skill/reports/context_budget.json create mode 100644 examples/governed-incident-command/generated-skill/reports/governance_score.json create mode 100644 examples/governed-incident-command/generated-skill/reports/revision_history.md create mode 100644 examples/governed-incident-command/generated-skill/scripts/build_incident_packet.py create mode 100644 examples/governed-incident-command/optimization/adversarial/trigger_cases.json create mode 100644 examples/governed-incident-command/optimization/baseline_description.txt create mode 100644 examples/governed-incident-command/optimization/blind_holdout/trigger_cases.json create mode 100644 examples/governed-incident-command/optimization/dev/trigger_cases.json create mode 100644 examples/governed-incident-command/optimization/holdout/trigger_cases.json create mode 100644 examples/governed-incident-command/optimization/reports/description_optimization.json create mode 100644 examples/governed-incident-command/optimization/reports/description_optimization.md create mode 100644 examples/governed-incident-command/optimization/semantic_config.json create mode 100644 examples/governed-incident-command/raw-workflow.md create mode 100644 examples/simple-note-cleanup/design-summary.md create mode 100644 examples/simple-note-cleanup/generated-skill/SKILL.md create mode 100644 examples/simple-note-cleanup/generated-skill/agents/interface.yaml create mode 100644 examples/simple-note-cleanup/raw-workflow.md create mode 100644 examples/team-frontend-review/design-summary.md create mode 100644 examples/team-frontend-review/generated-skill/SKILL.md create mode 100644 examples/team-frontend-review/generated-skill/agents/interface.yaml create mode 100644 examples/team-frontend-review/generated-skill/references/checklist.md create mode 100644 examples/team-frontend-review/optimization/adversarial/trigger_cases.json create mode 100644 examples/team-frontend-review/optimization/baseline_description.txt create mode 100644 examples/team-frontend-review/optimization/blind_holdout/trigger_cases.json create mode 100644 examples/team-frontend-review/optimization/dev/trigger_cases.json create mode 100644 examples/team-frontend-review/optimization/holdout/trigger_cases.json create mode 100644 examples/team-frontend-review/optimization/reports/description_optimization.json create mode 100644 examples/team-frontend-review/optimization/reports/description_optimization.md create mode 100644 examples/team-frontend-review/optimization/semantic_config.json create mode 100644 examples/team-frontend-review/raw-workflow.md create mode 100644 failures/README.md create mode 100644 failures/document-export-vs-agent-skill/README.md create mode 100644 failures/document-export-vs-agent-skill/cases.json create mode 100644 failures/explain-not-package/README.md create mode 100644 failures/explain-not-package/cases.json create mode 100644 failures/future-outline-vs-build/README.md create mode 100644 failures/future-outline-vs-build/cases.json create mode 100644 failures/one-off-vs-reusable/README.md create mode 100644 failures/one-off-vs-reusable/cases.json create mode 100644 manifest.json create mode 100644 references/artifact-design-doctrine.md create mode 100644 references/authoring-discipline.md create mode 100644 references/autonomous-adaptation.md create mode 100644 references/distribution-registry-method.md create mode 100644 references/eval-playbook.md create mode 100644 references/gate-selection.md create mode 100644 references/governance.md create mode 100644 references/human-review-template.md create mode 100644 references/intent-dialogue.md create mode 100644 references/iteration-philosophy.md create mode 100644 references/non-skill-decision-tree.md create mode 100644 references/operating-modes.md create mode 100644 references/output-eval-method.md create mode 100644 references/output-quality-risk.md create mode 100644 references/output-visual-quality.md create mode 100644 references/packaging-contracts.md create mode 100644 references/pattern-extraction-doctrine.md create mode 100644 references/platform-capability-matrix.md create mode 100644 references/prompt-engineering-doctrine.md create mode 100644 references/qa-ladder.md create mode 100644 references/reference-scan.md create mode 100644 references/regression-cause-taxonomy.md create mode 100644 references/resource-boundaries.md create mode 100644 references/review-studio-method.md create mode 100644 references/review-waiver-method.md create mode 100644 references/runtime-conformance-method.md create mode 100644 references/skill-archetypes.md create mode 100644 references/skill-atlas-method.md create mode 100644 references/skill-engineering-method.md create mode 100644 references/skill-ir-method.md create mode 100644 references/skillops-decision-policy.md create mode 100644 references/systems-thinking-doctrine.md create mode 100644 references/telemetry-drift-method.md create mode 100644 references/trust-security-method.md create mode 100644 references/user-memory-policy.md create mode 100644 registry/examples/yao-meta-skill-1.0.0.json create mode 100644 registry/index.json create mode 100644 registry/index.schema.json create mode 100644 registry/package.schema.json create mode 100644 registry/packages/yao-meta-skill.json create mode 100644 reports/adaptation_approval_ledger.json create mode 100644 reports/adaptation_proposals.json create mode 100644 reports/adaptation_proposals.md create mode 100644 reports/adaptation_regression_report.json create mode 100644 reports/adaptation_regression_report.md create mode 100644 reports/adoption_drift_report.json create mode 100644 reports/adoption_drift_report.md create mode 100644 reports/architecture_maintainability.json create mode 100644 reports/architecture_maintainability.md create mode 100644 reports/artifact-design-profile.json create mode 100644 reports/artifact-design-profile.md create mode 100644 reports/baseline-compare.json create mode 100644 reports/baseline-compare.md create mode 100644 reports/benchmark_methodology.md create mode 100644 reports/benchmark_reproducibility.json create mode 100644 reports/benchmark_reproducibility.md create mode 100644 reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.html create mode 100644 reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.json create mode 100644 reports/blind-human-review-2026-06-29/README.md create mode 100644 reports/blind-human-review-2026-06-29/adjudication.md create mode 100644 reports/blind-human-review-2026-06-29/blind-pack.json create mode 100644 reports/blind-human-review-2026-06-29/case-01-support-triage.html create mode 100644 reports/blind-human-review-2026-06-29/case-02-monthly-revenue-reconciliation.html create mode 100644 reports/blind-human-review-2026-06-29/case-03-webinar-repurposing.html create mode 100644 reports/blind-human-review-2026-06-29/case-04-incident-postmortem.html create mode 100644 reports/blind-human-review-2026-06-29/case-05-pr-review-followup.html create mode 100644 reports/blind-human-review-2026-06-29/generate_pack.py create mode 100644 reports/blind-human-review-2026-06-29/index.html create mode 100644 reports/blind-human-review-2026-06-29/review-decisions-template.json create mode 100644 reports/blind-human-review-2026-06-29/review-decisions.recorded.json create mode 100644 reports/candidate_registry.json create mode 100644 reports/candidate_registry.md create mode 100644 reports/compiled_targets.json create mode 100644 reports/compiled_targets.md create mode 100644 reports/conformance_matrix.json create mode 100644 reports/conformance_matrix.md create mode 100644 reports/context_budget.json create mode 100644 reports/context_budget.md create mode 100644 reports/context_budget_summary.json create mode 100644 reports/description_drift_history.md create mode 100644 reports/description_optimization.json create mode 100644 reports/description_optimization.md create mode 100644 reports/description_optimization_suite.json create mode 100644 reports/description_optimization_suite.md create mode 100644 reports/eval_suite.json create mode 100644 reports/evidence_consistency.json create mode 100644 reports/evidence_consistency.md create mode 100644 reports/family_summary.md create mode 100644 reports/github-benchmark-scan.json create mode 100644 reports/github-benchmark-scan.md create mode 100644 reports/governance_score.json create mode 100644 reports/install_simulation.json create mode 100644 reports/install_simulation.md create mode 100644 reports/intent-confidence.json create mode 100644 reports/intent-confidence.md create mode 100644 reports/intent-context.json create mode 100644 reports/intent-dialogue.json create mode 100644 reports/intent-dialogue.md create mode 100644 reports/iteration-directions.json create mode 100644 reports/iteration-directions.md create mode 100644 reports/iteration_bundles/governed-incident-command/bundle.json create mode 100644 reports/iteration_bundles/governed-incident-command/bundle.md create mode 100644 reports/iteration_bundles/governed-incident-command/review.md create mode 100644 reports/iteration_bundles/team-frontend-review/bundle.json create mode 100644 reports/iteration_bundles/team-frontend-review/bundle.md create mode 100644 reports/iteration_bundles/team-frontend-review/review.md create mode 100644 reports/iteration_bundles/yao-meta-skill/bundle.json create mode 100644 reports/iteration_bundles/yao-meta-skill/bundle.md create mode 100644 reports/iteration_bundles/yao-meta-skill/review.md create mode 100644 reports/iteration_ledger.md create mode 100644 reports/output-risk-profile.json create mode 100644 reports/output-risk-profile.md create mode 100644 reports/output_blind_answer_key.json create mode 100644 reports/output_blind_review_pack.json create mode 100644 reports/output_blind_review_pack.md create mode 100644 reports/output_execution_runs.json create mode 100644 reports/output_execution_runs.md create mode 100644 reports/output_quality_scorecard.json create mode 100644 reports/output_quality_scorecard.md create mode 100644 reports/output_review_adjudication.json create mode 100644 reports/output_review_adjudication.md create mode 100644 reports/output_review_decisions.json create mode 100644 reports/output_review_kit.html create mode 100644 reports/output_review_kit.json create mode 100644 reports/output_review_kit.md create mode 100644 reports/package_verification.json create mode 100644 reports/package_verification.md create mode 100644 reports/portability_score.json create mode 100644 reports/portability_score.md create mode 100644 reports/promotion_decisions.json create mode 100644 reports/promotion_decisions.md create mode 100644 reports/prompt-quality-profile.json create mode 100644 reports/prompt-quality-profile.md create mode 100644 reports/python_compatibility.json create mode 100644 reports/python_compatibility.md create mode 100644 reports/reference-scan.json create mode 100644 reports/reference-scan.md create mode 100644 reports/reference-synthesis.json create mode 100644 reports/reference-synthesis.md create mode 100644 reports/registry_audit.json create mode 100644 reports/registry_audit.md create mode 100644 reports/regression_history.md create mode 100644 reports/review-studio.html create mode 100644 reports/review-studio.json create mode 100644 reports/review-viewer.html create mode 100644 reports/review-viewer.json create mode 100644 reports/review_annotations.json create mode 100644 reports/review_annotations.md create mode 100644 reports/review_waivers.json create mode 100644 reports/review_waivers.md create mode 100644 reports/route_scorecard.json create mode 100644 reports/route_scorecard.md create mode 100644 reports/runtime_permission_probes.json create mode 100644 reports/runtime_permission_probes.md create mode 100644 reports/security_trust_report.json create mode 100644 reports/security_trust_report.md create mode 100644 reports/skill-interpretation.html create mode 100644 reports/skill-interpretation.json create mode 100644 reports/skill-os-2-review.md create mode 100644 reports/skill-overview-v2-plan.md create mode 100644 reports/skill-overview.html create mode 100644 reports/skill-overview.json create mode 100644 reports/skill_atlas.html create mode 100644 reports/skill_atlas.json create mode 100644 reports/skill_os2_audit.json create mode 100644 reports/skill_os2_audit.md create mode 100644 reports/skill_os2_coverage.json create mode 100644 reports/skill_os2_coverage.md create mode 100644 reports/skillops/daily/2026-06-16.json create mode 100644 reports/skillops/daily/2026-06-16.md create mode 100644 reports/skillops/daily/2026-06-17.json create mode 100644 reports/skillops/daily/2026-06-17.md create mode 100644 reports/skillops/daily/2026-06-20.json create mode 100644 reports/skillops/daily/2026-06-20.md create mode 100644 reports/skillops/daily/2026-06-21.json create mode 100644 reports/skillops/daily/2026-06-21.md create mode 100644 reports/skillops/daily/2026-07-02.json create mode 100644 reports/skillops/daily/2026-07-02.md create mode 100644 reports/skillops/weekly/2026-W25.json create mode 100644 reports/skillops/weekly/2026-W25.md create mode 100644 reports/skillops/weekly/2026-W27.json create mode 100644 reports/skillops/weekly/2026-W27.md create mode 100644 reports/system-model.json create mode 100644 reports/system-model.md create mode 100644 reports/telemetry_hook_recipes.json create mode 100644 reports/telemetry_hook_recipes.md create mode 100644 reports/upgrade_check.json create mode 100644 reports/upgrade_check.md create mode 100644 reports/user_patterns.json create mode 100644 reports/user_patterns.md create mode 100644 reports/world_class_claim_guard.json create mode 100644 reports/world_class_claim_guard.md create mode 100644 reports/world_class_evidence_intake.json create mode 100644 reports/world_class_evidence_intake.md create mode 100644 reports/world_class_evidence_ledger.json create mode 100644 reports/world_class_evidence_ledger.md create mode 100644 reports/world_class_evidence_plan.json create mode 100644 reports/world_class_evidence_plan.md create mode 100644 reports/world_class_evidence_preflight.html create mode 100644 reports/world_class_evidence_preflight.json create mode 100644 reports/world_class_evidence_preflight.md create mode 100644 reports/world_class_operator_runbook.html create mode 100644 reports/world_class_operator_runbook.json create mode 100644 reports/world_class_operator_runbook.md create mode 100644 reports/world_class_submission_review.json create mode 100644 reports/world_class_submission_review.md create mode 100644 requirements-ci.txt create mode 100644 runtime/conformance/README.md create mode 100644 runtime/conformance/schema.json create mode 100644 schemas/adaptation-proposal.schema.json create mode 100644 schemas/skill-interpretation.schema.json create mode 100644 schemas/skillops-opportunity.schema.json create mode 100644 scripts/adaptation_patch_safety.py create mode 100644 scripts/adaptation_report_contracts.py create mode 100644 scripts/adjudicate_output_review.py create mode 100644 scripts/apply_adaptation.py create mode 100644 scripts/benchmark_release_lock.py create mode 100644 scripts/build_confusion_matrix.py create mode 100644 scripts/build_skill_atlas.py create mode 100644 scripts/build_skill_atlas_layout.py create mode 100644 scripts/build_skill_atlas_opportunities.py create mode 100644 scripts/check_update.py create mode 100644 scripts/ci_test.py create mode 100644 scripts/collect_feedback.py create mode 100644 scripts/compile_skill.py create mode 100644 scripts/compile_skill_targets.py create mode 100644 scripts/context_sizer.py create mode 100644 scripts/create_iteration_snapshot.py create mode 100644 scripts/cross_packager.py create mode 100644 scripts/cross_packager_contracts.py create mode 100644 scripts/description_optimizer_reporting.py create mode 100644 scripts/diff_eval.py create mode 100644 scripts/emit_telemetry_event.py create mode 100644 scripts/evidence_consistency_artifact_roles.py create mode 100644 scripts/evidence_consistency_core.py create mode 100644 scripts/evidence_consistency_phase_queue.py create mode 100644 scripts/evidence_consistency_release.py create mode 100644 scripts/evidence_consistency_skill_os2_review.py create mode 100644 scripts/evidence_consistency_world_class.py create mode 100644 scripts/export_skill_ir.py create mode 100644 scripts/github_benchmark_scan.py create mode 100644 scripts/governance_check.py create mode 100644 scripts/html_rendering.py create mode 100644 scripts/import_output_review_decisions.py create mode 100644 scripts/import_telemetry_events.py create mode 100644 scripts/init_skill.py create mode 100644 scripts/judge_blind_eval.py create mode 100644 scripts/lint_skill.py create mode 100644 scripts/local_output_eval_runner.py create mode 100644 scripts/optimize_description.py create mode 100644 scripts/output_review_privacy.py create mode 100644 scripts/prepare_output_review_kit.py create mode 100644 scripts/prepare_world_class_submission_kit.py create mode 100644 scripts/probe_runtime_permissions.py create mode 100644 scripts/promotion_checker.py create mode 100644 scripts/propose_adaptation.py create mode 100644 scripts/provider_output_eval_runner.py create mode 100644 scripts/python_compat_check.py create mode 100644 scripts/reference_synthesis_markdown.py create mode 100644 scripts/registry_audit.py create mode 100644 scripts/render_adoption_drift_report.py create mode 100644 scripts/render_architecture_maintainability.py create mode 100644 scripts/render_artifact_design_profile.py create mode 100644 scripts/render_baseline_compare.py create mode 100644 scripts/render_benchmark_reproducibility.py create mode 100644 scripts/render_context_reports.py create mode 100644 scripts/render_daily_skillops_report.py create mode 100644 scripts/render_description_drift_history.py create mode 100644 scripts/render_eval_dashboard.py create mode 100644 scripts/render_evidence_consistency.py create mode 100644 scripts/render_intent_confidence.py create mode 100644 scripts/render_intent_dialogue.py create mode 100644 scripts/render_iteration_directions.py create mode 100644 scripts/render_iteration_ledger.py create mode 100644 scripts/render_output_risk_profile.py create mode 100644 scripts/render_portability_report.py create mode 100644 scripts/render_prompt_quality_profile.py create mode 100644 scripts/render_reference_scan.py create mode 100644 scripts/render_reference_synthesis.py create mode 100644 scripts/render_regression_history.py create mode 100644 scripts/render_review_annotations.py create mode 100644 scripts/render_review_studio.py create mode 100644 scripts/render_review_viewer.py create mode 100644 scripts/render_review_waivers.py create mode 100644 scripts/render_skill_interpretation.py create mode 100644 scripts/render_skill_os2_audit.py create mode 100644 scripts/render_skill_os2_coverage.py create mode 100644 scripts/render_skill_overview.py create mode 100644 scripts/render_social_preview.py create mode 100644 scripts/render_system_model.py create mode 100644 scripts/render_telemetry_hook_recipes.py create mode 100644 scripts/render_weekly_curator_report.py create mode 100644 scripts/render_world_class_claim_guard.py create mode 100644 scripts/render_world_class_evidence_intake.py create mode 100644 scripts/render_world_class_evidence_ledger.py create mode 100644 scripts/render_world_class_evidence_plan.py create mode 100644 scripts/render_world_class_operator_runbook.py create mode 100644 scripts/render_world_class_preflight.py create mode 100644 scripts/render_world_class_submission_review.py create mode 100644 scripts/resource_boundary_check.py create mode 100644 scripts/review_studio_action_evidence.py create mode 100644 scripts/review_studio_actions.py create mode 100644 scripts/review_studio_data.py create mode 100644 scripts/review_studio_formatting.py create mode 100644 scripts/review_studio_gate_contract.py create mode 100644 scripts/review_studio_gate_helpers.py create mode 100644 scripts/review_studio_gates.py create mode 100644 scripts/review_studio_layout.py create mode 100644 scripts/review_studio_output_review.py create mode 100644 scripts/review_studio_panels.py create mode 100644 scripts/review_studio_skillops.py create mode 100644 scripts/review_studio_waivers.py create mode 100644 scripts/review_studio_world_class.py create mode 100644 scripts/review_viewer_data.py create mode 100644 scripts/run_conformance_suite.py create mode 100644 scripts/run_description_optimization_suite.py create mode 100644 scripts/run_eval_suite.py create mode 100644 scripts/run_output_eval.py create mode 100644 scripts/run_output_execution.py create mode 100644 scripts/simulate_install.py create mode 100644 scripts/skill_ir_paths.py create mode 100644 scripts/skill_os2_coverage_markdown.py create mode 100644 scripts/skill_report_charts.py create mode 100644 scripts/skill_report_i18n.py create mode 100644 scripts/skill_report_layout.py create mode 100644 scripts/skill_report_metrics.py create mode 100644 scripts/skill_report_model.py create mode 100644 scripts/skill_report_sections.py create mode 100644 scripts/skill_report_sources.py create mode 100644 scripts/skill_report_world_class.py create mode 100644 scripts/skillops_opportunity.py create mode 100644 scripts/summarize_user_signals.py create mode 100644 scripts/sync_local_install.py create mode 100644 scripts/telemetry_native_host.py create mode 100644 scripts/trigger_eval.py create mode 100644 scripts/trust_check.py create mode 100644 scripts/trust_check_scripts.py create mode 100644 scripts/upgrade_check.py create mode 100644 scripts/validate_skill.py create mode 100644 scripts/verify_package.py create mode 100644 scripts/world_class_evidence_contract.py create mode 100644 scripts/world_class_human_evidence.py create mode 100644 scripts/world_class_native_permission_evidence.py create mode 100644 scripts/world_class_native_telemetry_evidence.py create mode 100644 scripts/world_class_operator_runbook_coordination.py create mode 100644 scripts/world_class_phase_queue.py create mode 100644 scripts/world_class_preflight_layout.py create mode 100644 scripts/world_class_preflight_markdown.py create mode 100644 scripts/world_class_provider_evidence.py create mode 100644 scripts/world_class_repair_checklist.py create mode 100644 scripts/world_class_source_checks.py create mode 100644 scripts/world_class_submission_kit_rendering.py create mode 100644 scripts/world_class_submission_matrix.py create mode 100644 scripts/yao.py create mode 100644 scripts/yao_cli_adaptation_commands.py create mode 100644 scripts/yao_cli_config.py create mode 100644 scripts/yao_cli_create_commands.py create mode 100644 scripts/yao_cli_distribution_commands.py create mode 100644 scripts/yao_cli_operator_commands.py create mode 100644 scripts/yao_cli_output_commands.py create mode 100644 scripts/yao_cli_parser.py create mode 100644 scripts/yao_cli_parser_evidence.py create mode 100644 scripts/yao_cli_parser_operations.py create mode 100644 scripts/yao_cli_parser_operator.py create mode 100644 scripts/yao_cli_report_commands.py create mode 100644 scripts/yao_cli_runtime.py create mode 100644 scripts/yao_cli_telemetry.py create mode 100644 security/dependency_policy.md create mode 100644 security/network_policy.json create mode 100644 security/network_policy.md create mode 100644 security/permission_policy.json create mode 100644 security/permission_policy.md create mode 100644 security/script_policy.md create mode 100644 security/secret_policy.md create mode 100644 security/trust_policy.md create mode 100644 skill-ir/README.md create mode 100644 skill-ir/examples/yao-meta-skill.json create mode 100644 skill-ir/schema.json create mode 100644 skill_atlas/README.md create mode 100644 skill_atlas/catalog.json create mode 100644 skill_atlas/catalog.schema.json create mode 100644 skill_atlas/dependency_graph.json create mode 100644 skill_atlas/drift_signals.json create mode 100644 skill_atlas/no_route_opportunities.json create mode 100644 skill_atlas/owner_review_gaps.json create mode 100644 skill_atlas/policy.json create mode 100644 skill_atlas/route_overlap_matrix.csv create mode 100644 skill_atlas/stale_skills.json create mode 100644 templates/basic_skill.md.j2 create mode 100644 templates/complex_skill.md.j2 create mode 100644 tests/fixtures/github_benchmark_scan/bundle.json create mode 100644 tests/fixtures/governance_invalid_manifest/SKILL.md create mode 100644 tests/fixtures/governance_invalid_manifest/manifest.json create mode 100644 tests/fixtures/package_invalid_yaml/SKILL.md create mode 100644 tests/fixtures/package_invalid_yaml/agents/interface.yaml create mode 100644 tests/fixtures/package_missing_interface_field/SKILL.md create mode 100644 tests/fixtures/package_missing_interface_field/agents/interface.yaml create mode 100644 tests/review_studio_helpers.py create mode 100644 tests/review_studio_world_class_assertions.py create mode 100644 tests/snapshots/claude_adapter.json create mode 100644 tests/snapshots/generic_adapter.json create mode 100644 tests/snapshots/openai_adapter.json create mode 100644 tests/snapshots/vscode_adapter.json create mode 100644 tests/verify_adaptation_safety.py create mode 100644 tests/verify_adapter_snapshots.py create mode 100644 tests/verify_adoption_drift.py create mode 100644 tests/verify_architecture_maintainability.py create mode 100644 tests/verify_artifact_design_profile.py create mode 100644 tests/verify_baseline_compare.py create mode 100644 tests/verify_benchmark_reproducibility.py create mode 100644 tests/verify_compile_skill.py create mode 100644 tests/verify_conformance_suite.py create mode 100644 tests/verify_daily_skillops.py create mode 100644 tests/verify_description_optimization.py create mode 100644 tests/verify_evidence_consistency.py create mode 100644 tests/verify_failure_regressions.py create mode 100644 tests/verify_feedback.py create mode 100644 tests/verify_github_benchmark_scan.py create mode 100644 tests/verify_html_rendering.py create mode 100644 tests/verify_install_simulation.py create mode 100644 tests/verify_intent_confidence.py create mode 100644 tests/verify_iteration_directions.py create mode 100644 tests/verify_local_install_sync.py create mode 100644 tests/verify_operator_ux.py create mode 100644 tests/verify_output_eval_lab.py create mode 100644 tests/verify_output_execution_runs.py create mode 100644 tests/verify_output_review_adjudication.py create mode 100644 tests/verify_output_review_kit.py create mode 100644 tests/verify_output_risk_profile.py create mode 100644 tests/verify_package_verification.py create mode 100644 tests/verify_packager_failures.py create mode 100644 tests/verify_portability_report.py create mode 100644 tests/verify_promotion_checker.py create mode 100644 tests/verify_prompt_quality_profile.py create mode 100644 tests/verify_python_compat_check.py create mode 100644 tests/verify_quality_checks.py create mode 100644 tests/verify_reference_scan.py create mode 100644 tests/verify_reference_synthesis.py create mode 100644 tests/verify_registry_audit.py create mode 100644 tests/verify_review_annotations.py create mode 100644 tests/verify_review_studio.py create mode 100644 tests/verify_review_viewer.py create mode 100644 tests/verify_review_waivers.py create mode 100644 tests/verify_route_confusion.py create mode 100644 tests/verify_runtime_permission_probes.py create mode 100644 tests/verify_security_boundaries.py create mode 100644 tests/verify_skill_atlas.py create mode 100644 tests/verify_skill_interpretation.py create mode 100644 tests/verify_skill_ir.py create mode 100644 tests/verify_skill_ir_paths.py create mode 100644 tests/verify_skill_os2_audit.py create mode 100644 tests/verify_skill_os2_coverage.py create mode 100644 tests/verify_skill_overview.py create mode 100644 tests/verify_skill_report_charts.py create mode 100644 tests/verify_skill_report_metrics.py create mode 100644 tests/verify_skillops_opportunity.py create mode 100644 tests/verify_system_model.py create mode 100644 tests/verify_telemetry_emit.py create mode 100644 tests/verify_telemetry_hooks.py create mode 100644 tests/verify_telemetry_import.py create mode 100644 tests/verify_telemetry_native_host.py create mode 100644 tests/verify_trust_check.py create mode 100644 tests/verify_upgrade_check.py create mode 100644 tests/verify_weekly_curator.py create mode 100644 tests/verify_world_class_claim_guard.py create mode 100644 tests/verify_world_class_evidence_intake.py create mode 100644 tests/verify_world_class_evidence_ledger.py create mode 100644 tests/verify_world_class_evidence_plan.py create mode 100644 tests/verify_world_class_operator_runbook.py create mode 100644 tests/verify_world_class_preflight.py create mode 100644 tests/verify_world_class_submission_kit.py create mode 100644 tests/verify_world_class_submission_review.py create mode 100644 tests/verify_yao_cli.py create mode 100644 tests/verify_yao_cli_world_class.py create mode 100644 tests/world_class_human_fixtures.py create mode 100644 tests/world_class_native_telemetry_fixtures.py create mode 100644 tests/yao_cli_helpers.py create mode 100644 tests/yao_cli_report_refresh.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a301369 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,17 @@ +name: test + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + - run: python -m pip install --upgrade pip + - run: python -m pip install --requirement requirements-ci.txt + - run: make ci-test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc323ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +.DS_Store +dist/ +*.zip +__pycache__/ +tests/tmp/ +tests/tmp_*/ +tests/tmp_snapshot/ +tests/tmp_cli/ +tests/tmp_skill_overview/ +tests/tmp_skill_preview/ +tests/tmp_reference_scan/ +tests/tmp_iteration_directions/ +tests/tmp_review_viewer/ +tests/tmp_feedback/ +tests/tmp_github_benchmark_scan/ +tests/tmp_intent_confidence/ +tests/tmp_reference_synthesis/ +tests/tmp_output_risk_profile/ +tests/tmp_artifact_design_profile/ +tests/tmp_prompt_quality_profile/ +tests/tmp_system_model/ +tests/tmp_security/ +tests/tmp_baseline_compare.* +reports/release_snapshots/ +reports/telemetry_events.jsonl +.yao/ +evidence/world_class/submission-kit/ +evidence/world_class/submissions/ + +# Local business-skill experiments belong outside this meta-skill repo unless promoted intentionally. +geo-ranking-article-generator/ + +# Private or one-off pattern analysis reports should not be committed as factory evidence. +reports/*pattern-analysis*.md +reports/*research-plan*.md diff --git a/.previews/yao-meta-skill-2-comparison/index.html b/.previews/yao-meta-skill-2-comparison/index.html new file mode 100644 index 0000000..5fe5458 --- /dev/null +++ b/.previews/yao-meta-skill-2-comparison/index.html @@ -0,0 +1,1336 @@ + + + + + + Yao Meta Skill 1.0 vs Skill OS 2.0 + + + +
+
+
YYao Meta Skill 对比图
+ +
+ +
+
+

版本对比 · 2026-06-17

+

从技能创建器,升级为技能操作系统 2.0

+

+ 1.0 的重点是把重复工作流整理成可安装、可阅读、可跨平台的技能包。当前 2.0 版本已经把范围推进到 + Skill IR(技能中间表示)、编译适配、评测实验室、Review Studio(评审工作台)、证据账本、包体验证和发布门禁。 + 核心变化是从“生成一个技能”变成“治理一个可测试、可审计、可迭代的技能系统”。 +

+
+ +
+ +
+
+
+

核心指标

+
+
+
+
+ 目标平台 + 3 → 5 + 从 OpenAI、Claude、Generic(通用平台)扩展到 Agent Skills(代理技能规范)和 VS Code(编辑器环境)。 +
+
+ 兼容检查 + 3 → 6 + 当前 registry(注册表)记录 6 项 compatibility pass(兼容通过)。 +
+
+ 评审工作台 + 16 + Review Studio(评审工作台)汇总 16 个门禁,无阻塞,3 个 warning(警告)。 +
+
+ 证据一致性 + 39/39 + evidence_consistency(证据一致性)决策为 consistent(一致)。 +
+
+ 复现实验包 + 25 + required artifacts(必需证据产物)全部存在,missing(缺失)=0。 +
+
+ 世界级评分 + 91 + Review Studio(评审工作台)评分高,但不是 public claim(公开宣传)许可。 +
+
+
+ +
+
+
+

图表分析

+
+
+ +
+
+

能力雷达

+

1.0 在创建链路上已经可用,2.0 在评测、治理、发布安全和运营闭环上明显拉开差距。

+ + + + + + + + + + + + + + + + + + 方法论 + 评测 + 治理 + 发布安全 + 运营闭环 + 跨端适配 + + + + 1.0 定性能力面 + + 2.0 当前能力面 + + +
洞察:2.0 的提升集中在“评测、治理、发布安全、运营闭环”,这也是从工具变成系统的核心证据。
+
+ +
+

证据阶梯

+

2.0 的证据链从文件存在性扩展到执行记录、hash(哈希)、安装模拟、复现实验和公开声明约束。

+ + + + + + + + 兼容项 + + + 3 + 6 + + + 评审门禁 + + + 基础 + 16 + + + 证据一致性 + + + 未计 + 39/39 + + + 复现实验包 + + + 未计 + 25 + + + +
洞察:2.0 把“能不能发布”拆成可检查的证据包,而不是只看技能文档是否完整。
+
+ +
+

2.0 工作流

+

新的流程按照从上游输入到下游发布的顺序展开,避免报告和门禁散落在多个无关联文件中。

+
+
输入归一工作流、提示词、转录、文档先进入 intent(意图记录)和 Skill IR(技能中间表示),不直接堆进 SKILL.md(技能入口文件)。
+
编译适配由 Skill IR(技能中间表示)生成 OpenAI、Claude、Generic(通用平台)、Agent Skills(代理技能规范)、VS Code(编辑器环境)等目标平台产物。
+
评测实验触发、输出质量、失败披露、模型运行、token(上下文用量)和耗时证据进入报告。
+
审计工作台Review Studio(评审工作台)汇总 16 个 gates(门禁),给出阻塞、警告、行动项和证据路径。
+
受控发布package verification(包体验证)、install simulation(安装模拟)、release lock(发布锁定)和 public claim guard(公开宣传守卫)共同决定发布边界。
+
+
洞察:2.0 最重要的结构变化是“先建模,再编译,再验证,再发布”,这让后续自动化和团队协作有稳定接口。
+
+
+
+ + + +
+
+
+

维度对比

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
维度1.0 版本2.0 版本显著变化
产品定位面向个人和团队的 技能创建、重构、评估、打包助手,强调把流程转成 SKILL.md(技能入口文件)、reports(报告目录)和 metadata(元数据)。面向团队发布的 Skill OS(技能操作系统),覆盖创建、编译、评测、治理、发布、反馈和证据追踪。从“产物生成”升级为“生命周期治理”。
架构模型以 SKILL.md(技能入口文件)、agents/interface.yaml(代理接口配置)、manifest(清单)和基础 reports(报告)为主,文件结构相对直观。引入 Skill IR(技能中间表示)、target compiler(目标编译器)、adapter(适配器)、gate contract(门禁契约)、release evidence(发布证据)、world-class ledger(世界级证据账本)等模块化结构。从文件模板升级为中间表示驱动的工程系统。
跨端适配registry(注册表)快照记录 3 个目标平台:OpenAI、Claude、Generic(通用平台)。当前 package(包体记录)记录 5 个 targets(目标平台),compatibility(兼容性)记录 6 项 pass(通过),增加 Agent Skills Compatible(代理技能兼容规范)和 VS Code(编辑器环境)。覆盖范围扩大,适配结果可被 registry(注册表)读取。
评测体系已有概览、信任报告、conformance matrix(符合性矩阵),但评测链路较轻。增加 trigger eval(触发评测)、output eval(输出评测)、benchmark reproducibility(基准复现)、provider evidence(真实模型证据)、blind review pack(盲评材料包)和 failure disclosure(失败披露)。从“检查文件是否像样”升级为“验证输出是否可靠”。
报告体验提供 overview HTML(概览网页)和 Review Studio HTML(评审工作台网页),偏概览和基础审计。升级为双语 Skill Overview v2(技能概览第二版)、Review Studio 2.0(评审工作台第二版)、review viewer(评审查看器)、图表化 evidence(证据)和 action cards(行动卡片)。报告从说明页升级为可审计工作台。
发布门禁1.0 registry(注册表)中 archive_verified(归档已验证)=false,package_verification(包体验证)为空。archive_verified(归档已验证)=true,install_simulated(安装已模拟)=true,package_verification(包体验证)和 install_simulation(安装模拟)均有报告,failure(失败)=0。从可打包升级为可验证、可模拟安装。
安全边界已有 trust_report(信任报告),但主要是基础扫描和文档式边界。153 个脚本纳入 trust scan(信任扫描),secret findings(敏感信息发现)=0,permission required(所需权限)3 项且 approved(已批准)3 项,help smoke(帮助命令冒烟测试)86/86 通过。安全从静态声明变成门禁数据。
运营闭环更多依赖人工复盘和手动记录。增加 adoption drift(采用漂移)、metadata telemetry(元数据遥测)、SkillOps(技能运营)日报/周报、adaptive proposal loop(自适应改进建议循环)和 release runbook(发布运行手册)。从一次性发布升级为持续迭代机制。
+
+ +
+
+
+

使用场景

+
+
+
+
+ 从零创建 +

把重复工作流沉淀成技能

+

当团队已经反复执行同一类提示词、文档整理或代码审查流程时,2.0 会先抽取意图、输入输出、边界和证据,再生成可安装的技能包。

+
+
+ 升级治理 +

把个人技能升级成团队资产

+

当一个技能要被多人复用时,2.0 会补齐接口、清单、适配、信任扫描、评测报告和 Review Studio(评审工作台),让维护责任变清楚。

+
+
+ 发布验证 +

给发布前的技能做证据闭环

+

当技能准备进入测试版或外部试用时,2.0 会检查包体验证、安装模拟、兼容矩阵和公开声明边界,避免“看起来能用”但证据不足。

+
+
+
+ +
+
+
+

优势分析

+
+
+
+
+

核心增量

+

2.0 将技能从单个文件集合提升为有中间表示、有门禁、有证据账本、有发布边界的系统。它牺牲了一点轻量感,换来更强的可审计性和可迁移性。

+
+
    +
  • 更稳的输入边界先确定任务意图、触发方式、输入文件和排除项,再生成技能,减少“描述很宽、执行很散”的问题。
  • +
  • 更强的质量证据通过触发评测、输出评测、复现实验和失败披露,把“好不好用”转成可以复查的证据。
  • +
  • 更清楚的发布口径区分测试版就绪、公开宣传就绪和世界级声明就绪,避免把内部验证夸大成市场承诺。
  • +
  • 更低的迁移成本通过目标编译器和适配器,把同一套技能意图输出到 OpenAI、Claude、Agent Skills、VS Code 等环境。
  • +
+
+
+ +
+
+
+

流程拆解

+
+
+
+
识别阶段确认这是重复 workflow(工作流)而不是一次性任务,并记录使用人、触发语、输入文件、输出格式和不做什么。
+
生成阶段生成 SKILL.md(技能入口文件)、references(参考资料)、scripts(脚本)和 reports(报告),但只为已证明需要的能力增加目录。
+
评测阶段用触发样例、输出样例、失败披露和真实模型调用检查技能是否能稳定完成目标,而不是只检查文件是否存在。
+
发布阶段运行包体验证、安装模拟、兼容检查和证据一致性检查,明确这是测试版、生产版还是仍需补证据。
+
+
+ +
+
+
+

底层原理

+
+
+
+
第一层:意图建模从原始材料中抽取任务目标、适用场景、触发方式、输入输出、边界和失败模式,形成可审查的技能意图。
+
第二层:中间表示用 Skill IR(技能中间表示)承载结构化信息,让后续编译、评测和报告不依赖某一个平台的文件格式。
+
第三层:目标编译通过 target compiler(目标编译器)和 adapter(适配器)生成不同运行环境需要的技能文件、接口配置和清单。
+
第四层:证据门禁通过触发评测、输出评测、信任扫描、包体验证、安装模拟和 Review Studio(评审工作台)形成发布前证据链。
+
第五层:运营反馈通过采用漂移、元数据遥测、改进建议循环和 SkillOps(技能运营)报告,把发布后的问题带回下一轮迭代。
+
+
+ +
+
+
+

示例说明

+
+
+
+
+

案例一:内容工作流技能

+

输入是一组文章改写提示词和历史输出。2.0 会识别触发语、输入文档类型、质量标准和失败样例。

+
    +
  • 输出:SKILL.md、接口配置、示例报告、触发评测。
  • +
  • 价值:减少“什么文章都能改”的过宽承诺。
  • +
+
+
+

案例二:团队审查技能

+

输入是代码审查规范、历史 PR 评论和团队约定。2.0 会把评审维度、阻塞项、警告项和豁免规则固化。

+
    +
  • 输出:Review Studio(评审工作台)、豁免记录、行动项。
  • +
  • 价值:新人也能按同一套判断口径执行。
  • +
+
+
+

案例三:外部分发技能

+

输入是准备发布的技能包。2.0 会检查归档哈希、安装模拟、兼容矩阵和公开宣传边界。

+
    +
  • 输出:包体验证、安装模拟、证据一致性报告。
  • +
  • 价值:让测试版发布有证据,不靠人工印象。
  • +
+
+
+
+ +
+ + + diff --git a/.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2-en.png b/.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2-en.png new file mode 100644 index 0000000000000000000000000000000000000000..853fc87998f5342a9306f953d49c16ac31c56768 GIT binary patch literal 789579 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

lkdS_xF3~35-g<%8$f6=N^j(T`C@v`~n2ib}|= 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ⅆcjTvT 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

bnrctfh_~WJjzk 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%0D?`2XZ1QKg3Z|3iTjTw}U$H34L_JZB;Mv<&t zVszJ>HyQ?-;Z^PP!UIg%O{NT$9o9jr;jRPndEgj4b>&|40jtW~nBHai4%at2oy=lE zG1QuF0P+C3`*P}9ISOGXQA0~rzfyhqy0UX=P#KlhZWDspaiO(2*4gX#eTVIKbGoTS z*@dt+m8W{vH<1hOnCu?MIxCQeIm`qWcC6w4_znTP7%9L_g7GjpXf`%BHPM1pJ;4Y$ z2@={tt2m<^BVVC4n4C48thhh7D*KiEFcv+f-)vt)j`Q z$wlICm0=M%ydI)$%ieM#M2dw@*i5}zKwt;Fc_Chs;(qPwLd>^)UZ7ZLh|oanNRTkuD+4@qY~w`tKGy~a6FIg*eX!0o9n0! zo0k)P9rN_ZeyakMeie3?TLg@9no+LT$wyez?^?GZ53k_|r@rjf&NQsl{k*sS5GaWF z{uj6Nvd2)c7A?l2DIp0-k7o#|j6qW5UG6)}b@~b`xIONVnKp`>zcp9H*Jr=Q76CHN zKxAw?JNl4G2L{8A5dcE7y!swOmYmJ86 z#=9)hpx*HqSJ@#feNiY*s7AXkPGGbi<~an77zWGf;G0)~<T2_AS8Jxjd5^WuEN0ZnN!w+<4mB*FoSw<$*869txHxz(t9bwtk@ln$e5*NXGh1A>HYoj zX*}uaSnqcJP#aJvdEQK6)JR4&f4?P@HYyX^vrZ3n13v{n$)lbaOZMl?|MT@KScEj| z8TN}GM}JsG@01jXogK%2AQ2D?_O&F13w#q0EsUe%K(z z$K!*25#h%iLXsU=xDU)gw;CF_hzc1yB(Vv61 zb>ceDp~>l)U<5V!8lX72z~($TY2fWm8%FZ$%(1H4^*i-_2=2Mg!u z;D8RrIQI8o;1O^p-}34s!u~yMPz>rG9u%n~!f>#YufQp|=oOLXKmW&v1C^FWLtFlS zB{Vu5z=SWJtE54z{xPWa`D^_>h)ACp{KX|j+XsSpDgT^V5pi*3G_-$G)qx--?aj;X zUDN#kFh25xYbOq9f8P(OjEe`vD98Ue>pLpXW(3ii}e1B6oCPp z|9)dgqzq9V?0>F?H+cU!N&gd2*{i<7AJPN0$rz2lR)+4VU$=(ooGLq5c*KU2gHh_4!ic`MJa8NKp~Rt^DN#U?&npL`8x8Gc+(b zBRRv+L`uL#0GR0xas=?lKptb9c1(DmPmEt_wdwGrQIOj>!*pyM|YkY~aU2t(ELtzKU!ER5kG)Pn`A-zfSJI zEYKt2o=_96%eKvGTpXHNyREGklPIe|GB8JC8GatBLZyDr_x0OcyaMZ72^}Y=WDW7X zTX}&9)R}&|*ZuXXAP>};x~tnVaPY!r>O$s=z}bL#`LZmntqw9$x?Z*^OzpC}e?q-< zzRP5$*tWm&9=5msIx8yonKifXwqYK$9ce$k{_P(bIY5qGdw=nQi7?~SAke^F@8M$A zn6*4=dAr_IUtbSET?nXLt&u7!oCD?R_jb5j27t+pwfnHwSZC4zhJhLQV}ZC}Cwd@u z;yQ?HsnYo>d7;*@7Uko|CX=7fw_*i_1RLsq>q9`U#6PHyVfYAb=luyQ{u&Ur6vWyt zG?H-OC-tF1T3T=pZ?MS+7vPs0w;i7zo3W^p3ZaR_ReiBIuC-lM;sTMgVVjN-j<39i z@4s>oE}xYctIVbr)BnYN5-N$lz<&_r~f{vf0!W90T|S@uM5pw zs_7ztd+BDni{9K_!s&20%q@Rg2I~K25Fue4u=_%kpwW+9srj8Coj31aT-kL%&K>sth=fiCPHVCY6mTFXb!(^R%J zp{7R3$Vh4Q&W2K`P@}J1O-&7M11`E3?7u#mNDM)}IfCkyS90m|c3u&-ag=62a>27XvBZNF)^hui9s5}eHFPKX#mH<%~b+25B; z;1C2UJCcb`H8n)FR&y0j4{lE0fQ)1cBWdLAOIop+`!%yJ8Fl^r`upo;ga*fbV6Wua z-PWKWQ7tI%pPLJ*u9Uwd`;DRKTSP(3$w&zXe1Vexrh`TN_tjR}!8FA1HPKbq2B~7b znT%bOqW+$8d}`6PrMFT^$sl#F?%HPm&QYr{z1Om|eX1NXCgQ&1Rf)J}Ec|bFaQ)`1 z(5N~fWyNWYDVTL05T6-Rf>|b-u@sz=Z<1klQ^u2 zM=(5}FakWR%qIX;zWTN@;0)FTLRaRLqcf5t?@wLkt<1Z{q_3TDFH~B7SBXTCjm|7I zxVY$Vi`l;Id*?RjWLseK)JLwCX1y&GL~@Z6;2x$O8sD1e31`#(>vH{db#_4hz@ivR zWr14yB)7WMb&_^=ZXvzfVafj0{Z8h17i{wb8i;K!pg%+i)kIo}D06%Qd6?S{X%21; zCbNT--n^+MMqnHKdODOJj@<9LcGH`;@IWQ>@s8vbhS5-xp`M<^1i*J3T<^Ot%)0_n z_Gk0u1>Ntc%eAMAG{Ib<>CxR)5DGaH2oAmVkMTf9b6IV_866!3-q!I5BsIH*+L7`3 z@tW_y`w&S`D77Nu;_947aJ;-)PdCYT_I4HR@)WAos!io{M;W~u?jipL74X=yk2?Sg zpO~1a+4F9CxP&952pDWd=&P$c%W-slZI&9UtJ*+A`R3jln=B;Ah3*462F=c~*N(pF zvwBv$t-%yS>!age-ps!RRoNTKW2xt3C|HdKS%Ayu7HEztXG&e;)Kx%UO^odB|A{pT zhx67ionIl$o*f;Ul#2vb45iS=+Fg`vCl4r>7z7HGloVq!oAND$4~!?p4u5-%5Q@cy zl=?#^+blYqKv?!%z*bZ30AMNS+5e5DG$sa;uGCm8OLz}goim_rHy~YQtz92;Im{d` zfxO*Z3cXfktI`fGpQRRi^Y-rnAPS|@XxL<>r32jA))3cBt5$%v9vp3_gIV5uwK5$p zOG^;_c}@x7A8;ei1c=W}$r2``RThOia-q_M(a>dM{qD|jDwliAd)$>O)w9E;M*4`? z;!BH*9-gUl(d4CF$V5rBp7Uj^1X@jR`~w3qK<1Z#Kqsge;&OVZERB_eox!wE5i8<| zh?UM<>qqN#fq7>$f^lq~;bJO~Optl6fF>arM;K;>hVQE_`(1$oW^x_s4 zVqs$wggh>KwLt|AjHECYhT12iK6_@p{BkN^-W|l7Dfo02rm{dlDib+*A!wXt=j4L^ zzMRG(NImoe4Z!}n=%d+m{yw>ElN^SLtd*3csDEjq39jV^PjW^gLlE?!G(8P!3E?Q@ zo<4f4%hDV>|EUEO^sW}GGl8l}8isiCW#UY!dX1T2J%jm{#BB_Cipo9@4hh+WN&d=q za$*~VN|}|TT6b;QXUZ!IZfZW_cD-jgG}o{Z@&DHRQ1=!w zua1?uaMp1nOBF2akXT&4zlZT14U;|B&h0m(TJq3H-$iTe&Md4>VKCV$sYeG`4bIY0ey75AFJb?*HGGalLIMvdvWMrL-~-4q!@G4TS&E|BO(#YBS0l+I9^A$tAX;EhsU&2PF`!E|o85jVFve18 zyWxI@99Yc^C+q5_#!zdI)p#~0vv(pykMcZRklP~Jove8cN(_~_Yh;=>D76|5?ZIQS zQ)-vvpKII%>`%#}eN?huYZ-*RK%Wfq;Fs)ft` zLh72!G=gW53yA53LJ=fy?`Q{v$;VTs@4g`DbEr=Q)lEF5^c+Z|*L=>JpZ3m_k6K9? zI`RkydtE>`8pNxC6idmQqPj#WDJf7SsxB*wqc=XRv1AWKC5JrBSD%3cl0;*Ax{(k3 zUf|+c?H$L89R`6eq$`tFcqv=ccgPrhrdgD^9%5D(vi)S$34EnMMVw8*3EU$)>1KlOdBl3Dl`#kX8!D_d<(z_i0kiuri zWhqO&9L~0C_O!YKLpd>2>OzRq)6?KcsHm!1FSLAvF4tCQX%{LtD>nq#XtP$cn|$xV z;I90^z7c80`QyXg+jWxZV(l5EzEkhG;1sC!)~6kLAa-F+5oCFb zgW6ooW99ea?{>Sf8Ep38EQ8OIWHQ^*ehF$Ol)!pICS4nf2_$Uz#uh8P%3rVDI|9QD#PRUJb=(rc_f)#9P!!X zv#uc2Tpqkcs9}u#r^QyY>b=UX!M84lN89^gk!FaVfsasm6}~FbTJi2PdFDoz!JZ;k za#9S%jbFkBaKf-K56n5q|_bh1PR##VpPN7%_iCT(~48T+1-~jZ=s;#_TvaN{`C`0x}u)TVJ8sz+BcAFbuskdIcUV?;(_{{@E_9Hea0SiMGx*_E-wj$LXtSm zl|a4=C1opTni#U^r7DLQTv#bVBcp7Nf9@$f#XO(*j1rM}G*}&}WD13twAW-&vd1)f zJwYOgB{eiOOd5ni4g;j+a}EDR>KYlC9Ho?PSL(8=u$D2px#sw6VCi|(g8J?onE5?g zIx|~o`DPI_PRB}-{Cau$68SesROX#PB#|`oTM68cAycpUOO71uY^b#Ib^z9gYyn#h zJVO@SePFS$lQlHe<@HQ2s5j~?!p!5b+7)plyO&;3_( z^ga>e71flix@!UnZ{XoW!@{IRC7T^~^~}U7YHP249k|nOd|YX8Txd*50j@t@(KwJI*ZsVG7d^hszWU6TT);VMy5M zVO3bQoy<Mk9p8f<69vqZGeR3AOD^3Brq*rk7g6|(vL7VI6UCR|9U^d2+ zh|V<7Ua^oOHaP6^oh1Az%{QIar)n>`T|{L+O&2E?FI3q^z3fsd(JC%1yaVnPG(^{0Wkx$@fJ z3tSqVPMgvnKi>C8;vx=hyf@Cx!Ut{$XAZAJEJt(d7uqOfY8&#iB>fEu5ziIDTxCk& zH{#C9 za-VE4Uj^w&19Zd%^iIG&$ONm-toqvMM_wK&EG+CC34gRAWj{G0!NkGyjH35rSp57i zG&PeCh+G4;#Ret|6eF>#fsjP*89F+KoUW<5Y6u!@U~{W9?TPBgapoe^v#N@5dgK9e z685+s@1@_;(?lU0nHjE#i`p<~BoNBc+AI_ET7ekFIK=J-&1OyHAgRA62t|O1 zn-9cL9xc`*Vj`+%+ZkTg)2PPN(e4xtO0XG;PLSOMN_3rzgGGZFY?PUric4I5J*yy2 zWkutwIV<^u?KKQiB<^4#*r4xpn}uh;J>+f$)gr1yoaJk1#pJpkV+j(7D3yk#LiqlV5-XkS{$2-D4v zyf+#bDo~=h>81A9(+V#?*?;6rrK)#NjbZ%g;niWANGz3_x$Y~m(DeJ)6lYIqZnkj~ zgdAd%^TWp_&F<=C0P`xfhlIG+Ol9fHkks=-vcq^Rw2u(Y4|>D?7(fOz>EB%@GFy3E zU#}jge**x*FxBw6ELfa$aCsIeEtNN9%G=o3sF`69(Bo&UqTkt=08XI_=AH81RH?D4 zDe|@4eWWzleMX@BvCr7D0&z-VEHyCwg50Y6!{4=-FTt*-)#D~2DoTXpBxxZW#`DT* zf0|M{l}l1e>hj_OID{zWvPj(#VKB!`mKr1{xAV+#a1Kt9D$}xZvIr}^${Z(S9!M=F&5t{sw%xxMw8onGbb;XSftf2+Gcg#(a=Xp`c=fn}=nn)FW_Bvs z*&2J(j?T_c*SiE$g@6mHpLEpSCa?K47axMboWY=3@8;uq6`w>AkLCb~XtN?xoXFj)Ubz>wW+g)n3cDw^k zaD@p_B|EO$)6za|X9R3=Vy%@M_i@hF&GAnHQ}Q=%hz_gmuJc=C1j2d4QPE9h(lv#B z@H(E-_2uQVz>)m+ohONUQQ&4Cn_qzbg_)K6)o6*LvqO`^?sG?H3%q_}o|5i3OYSV9uCJ&TT|kHY5`2Hfr*<3Y*Ez-`odwMnC!%Gts+3) z`U=tdvS=6dW5uNZ^kKH0C}9*-<1jcqPcGL&dKd;%*_!lGl~17BdU;wK`13(+5Fw>L zvtxS*ccn5e#7BmZ&2HITr#8ok*w^9F`EWjSwOM^I!l@|9hi2Y?apQ%08Rh z6ju3&gHofnDPJ7@^3h^BgaA3uEH zg$1;A@5+-a==y$EE|J(x$K&mhN=6e+%5Vu-bHBV_$gx~_w?C80<_NSitBZ4$3>9GE z9Tzsz0T{MqHrKuD6MY5~K(;rfr;n+Y)00SzHtCN%q%xb16A}}54-PgN4J8C`ct`oZ z!x%OB4&CjvIUs*(a$j1QyZ!5zOr8v0pyikugKw2{9*A+8u1UVetIO&n&-24f`3{yK*x{_uS-i}J8<*=}ga=s-QTe8$2U76{T*-WlFUISt)E2?8F z%{bj}J8KlkE?aQsd%#FgTtb{!_v{Cx=i>g-q^bo`r1U|aZxjOgps1*Z$8%-GV<=n? z59+_Yeu9N@o5&VdE`aL)C@l(%xO2;#Ph2k7X@Co&qH;tY*w1C}715ebiHH~%IIT*H zTCwN8-MdOZLcr!ArFjpx!#&!o;t~5kb>!}-^~Ize86Z=CgL+>-BdI*1{8W8sJe~q# zU@R%Zqw`?~q*N^WGpOlE;=LV4K|)4; z*q!JBa#gwr_7r?B;m+>P^T$Gj%rDOqK=7QtIksi7Saksy0?j7$yuLPVzkWLjOO~S@ z_!$%VVU@rS=Z?e4)+t}9m`&a65C>;_I3?X*t|v4ZAHN;gF#)7R1je5J3_i3lTTVBb zRvJw*@!pIbFtSL5W?(=L zATx%DpOkmrp4fs=_NjuPt7IhxI)=|z>(a4yM*e#oS-}Oe9ewi+U>nk3&ueIS4%`S) zSxA@p-9LVP+EdH_TaF|(XWZR21*!wf=^}JRN@yU}&B`L&+kK1npw0N zz#c5lb|yXR)ph}Y=HW%gxGm8W^aG4%{L@=62WOU*noKpGFHeiEXW)5^<|rR?tsN|X z&3U!YZIiaPkoL$eTpGvSdRIUH2%4SREs%V2SF_sx2Q)Lq7ZUK7I?HfymzSwR1MU6cJkGr8n>gZ0TD zHsGKC2v|gX?HJPZp-!>?@CSe)mx=rN_ci<@3c(2*0ib{ayUYtqORbzY&i|@okZADJ ze?U6`_MihB{4+PV+rGY&f9VJ$QZll)0Q&eBOyCh07uVK_dyhNt?~i@<0T&k_2>&QZ z0A+!Ijjf3z{hF5I-`C^qYwghj|85fv$`RlpJeCd)j&Z?7vvD@wZE-y$Y%Wn4|NKQB zdoT?Ky4B$#aS=&r)HDL;Fq0zWWVF7|WKzMv+j*739!kD+mg#>ejBhb9C}BM_(_4Uq zT<_zRp@_m*`301qUs$ zXWR75;o=-1H8QvWQA!5V89DjLSJ2%YDPKlr#-_LaB|*~A>F5RdbbUn#B_ys3sA(q0 z#tfk*1^ylkzu z&87qN(jSeDM7V#?gzn$(rSYtu+yHtA`qRp)GJv?~=zTo6{v#WD1@~@K03YvPcnPRr z$N%e%{Q;u>!Is(xzW@D6AmHhLz)XY$2X1EKOv!Q0x4+JjU*}LbCRQ1gE(9dj&$r)S z2u~k1w{0vZzor%%S?FrBMh1xdow3OdJ8>yMNJu^guyBish=44B?)y^zz~EEJub~ZU zKL8W<2pl2qy8gnj}y=_q$r4V9X24`6;l(Pe7}NC-b5 zVn_N#<+Z7jkZn5Xb@!f^yY3qp6h>CTZX$iaIoTQGM=mTJkY@Lowxp>$`UDHn0dC zR&D|f62L*meeGWAcnIrGW38m6`#e5mi5sb$E1eAU7Ba9g1$G*IE1UQXqW2!2oQtJ& ztey2eIs$ibh3nWjQQ#;^t1{F9hNFwwJB92s6LHBJ?Ucu{CPk|bN+&~RVX5lb1x z@x%O;Yu-3;zMcSWYtyfP4k&m~z`UU>1_d%K;R{nD_A|rd9vH(wS(l)k4uaOb-BXIe zVAESql`8L+lI;2uzhkt845i==94aT;kM~>|MV7iwMmnRyuNyxec!ZPEZ**+0`B0kl zU3X6M#SmZNw0e9v_ukTOx~co&V_IysX;MBoSd@0PsurwNAK{a|@O?4D2;2RE6@gW> zOIVk>0Xiu~Ox8!i)6=v5)n80RAM$$cHdE*({%fjHDU1WB1}RdhIJud*IavJA?PXpJ z%%OnMxc|l8TZL7b)s$VM~ z&aFhb=r-sJp%HP0lL+*<4$S*=?26O$+HphsJ&Xz`>dM=Ed2|${C%z+6X{-{_ZXeO= z(utZ|CM+O^t<14ti_J9{H=wOA)_u7lnxX~1O1M`sY@$NAJ9~OmKUpP11{z6~LF2>K z!0=$ii>5^-PA&OSMcN?xrgS>Ezq6<0a0srSOL`3jsS5%RTc);37WCdr z<_bnfS8^V2V`vY^R9z}Lu@9J-ZmNy( zIj+>rQP+n@Bs@0UN$w^`k00hek$b-UDO&ZyTi&h=m^JZza=5GW(m6FX^{b>F;#(>s zCv)mq0*CD^gedm+_u={ubHA3}UKRk|)tf<>JLB1IbCvvFy))g!@ZD%~JIe;d=~FuGKLvK9BtuVz3^l zk@+dqVw1p-ur{$Kv;;MfKY=hTq4`)>_OEP{K6;h0cYI{&dz+j}HZ`hdfQP#!jNX=H zgnjX^SpWb)+}*!?xGz98VRQiOo**LjFQfI`6r4a*JRo8>A4fVeuE>~(w!lz2FE?)> zSDEa@!Ngbo8?c3&v3+50!*0?24z-pk5zXW@-$|p8-N^uRQQeiw9k|vVJMPsbl99oJ z=K-H*#yh}F#xc7_)YN=^du6*tY%2r; zh1a|4bhlI3 zQe0A&hOplJ<}2tIX{BhB&3Ez24I0X8CeUB%O3xSTg#9&JcN{E3HR@)yEwp9w&6F1g zX0q8oz(Tky{W>0i-Brtsx>EG7^J-E8rDiRG2UPdKaOmvo$(feU`gME51wnPR6M6Biesj=>;vPU?ddJj6uRD>hYz?>hG67}3Sq zKUJ2H>$=#Qa+zvm4@Rla^7)LriqesLCzL^?kh{Nrt>?aa>}*a?PlH*O%i)Nq>U$BmW|#J|#stS6Ma*dz z<}vVXw&ZcJX}_CGK&zC;#>AAuhcp|S*3!)K75_1Nk$BZ{?R5Q=hxOeg znUHiBvl`|k9{eTFM5KxLl&Ipy4DwaZ8-^>!7eX3EQbh7FM@myjWJD>r@Z7nW%Rx8! zUqAC1NmrGOFn*G)_Op(bg}8X_8zw^A-b`kxS79UP3JI_49lXTl;qTT1!KTii5bQi-Cj-SY`BZ&$W`G;2CF%yALPKp<(mz5Q)+ z?2c-t?5nn?NqgHR%uEp4E!H}%2DKtQVmt8}X_ycZX=0IZ7Buh5ZrCPZ&axsS ztF7VF>8Nu>U{f=Bp4Ow!&|X4A>-rw>hFT1lkwznTANV?XdDr84-9v&$$%KO6a}*nN zJ?Y63!W?3uq6m_-&B{;Uc7F4Qtc104Q{;Z0Dak5q4wm|=_F9!*YwGA47lutwzrtf& zWH%ekb-qrh#bVJLB{>>I!c*TbMEH)H;26A!H{bGg0KFb7`u}kAD}=YH)W?4T)iK zM%27LK`r|bg3kQ*?FPmZ5fO<%zdKSGzakXxyjvg%)GAc)9dc(V&GJXq^=UAFlx66u zWI2ZqpU~NMBI8$u#yyS6U%k7~<{;yhfNj4Ff<@4lMN+mFv11jY_;{{Y4hXgak3kh%Et+x2;&0#yY zX}>T3#e-+2C~O8lz*zHAbrh4nNsobqWWp2F3z9xK*H=VW3~etkfAps1m3kf!20`F| z=f-67vm_3$mgbPiw*ab5KTO_6n36Kclw^udRK$66&_}4D+w4EmQ0wg;N|G*bA>S#7 zA9&g|7;A!EH3d&@wULsAi2}X=9BR4;`5sjc?t#Yu5BdhFVcDp+wUNQq1aq)vClm6I z;k2(DP-k2?3i{YfRlB=u{(Z_bLYhq0jLH9@*51E|k+#$XbtiK#wwS~ek-S`O2~eBJ z8BBaMg&XMx%xM}ga)?k1YZcbQHj8xa_ zgc_F{#GKom$!|qM;*>_I)ANLGhwRTKk^|2uGC<=NI+o0bKnfI`l~xW}W0J)1c5h6T zJDZu&>3@eoQt1Kr?t)}PW6I!{Zk^MPlN`kyUQ<-utGQE;+NmY0xb(z75>rBm(4sHb zanUk&m&fLom%HoE&b2&dD~SmW`@R~cqx9>pR4dYjyD(CfX8&x%CBaOVgD7kn0Vn+8 zn$8Nsc9y?lLS2>irus^maV6Ah`w5YFWWF=b6MOlVE!TA>$E-w4b=*C z*r?+U1zPFcZW<=GD(r6AhgfyCt3?P9GIu_OZtm;gm$U04apG^3 zhVGnfF2~&aa?f+CGA)ZM723ryYW~dbob{LH43(Oygr+>i_Rv9oPI%ene7U{triZ5& zZhLcEo6A1kM^)2ER9;+crF@n`JL=o}Bgr+N+GoQm!;tV%EjxIa=XB~aK4E%}j(){5 zv-+~PH<+6a44ZWbvE&O@Ul!?63)vXnjF+i!b-Hx9Hm&$&-`e|h+ZTj%5dH;V4yh1f zNidUsOPO&`KFqWw2-&p_yQ6t9SB&t*nIVJOFy|)_!Gi4{6qG5)L+G55@zB-PwdZ{d z54-uy;vrlfW=PIUi<8iE8*IM>)=`04EsIOrOY|%VYVb~^Pa@t5kQ17tV|?pK($_Z* zXOJ7N2xJaO@xx}|*JgA*LWDWV?6!{0Zo$Pm3gjznPFyfoUB=Q6+(?3Ia2SqU%hzb>3=Ib$(^|>Pt@Zh7 zmi2wON)w^e+83J zJaQ_FY6mTB=>ppZy}D{w!=z3FqKmitowo*_0o))8S zynp0`ca#*MW}kBZCZ^P2l%?DI zokPkR#%54+no0wDzhm?Gl?_kswNc})Y;yBr*p`RwS2=PU8w@e&*ETlLtCfFlnk&pP zHRVA}oY}PKXHiKbFd}HwD-8lvx@Rz^4Y1YycYt8 znjC7hF6%#dW7(2llpCyY3R4Q}hl3ZxfH#OlhGk_Vv`HaHKD@o{Xm1TxI)MK3VjD?S zVstQohY*}pkfsx_Pk}9>sI)dxm;tfHz;Dfk_OWbX^kJba;_03EJLh__!WcEjpLSmx zt+WrtO=|c^0mMfYmyRDmkpo-i&hn5S;nmc%=Fva-J@h;G+heS`Nz+mat@_XFvr0H) z9Sg`sF1d0X-4T@yZ&CR8GOLx|)DVxDytYz0&qRTp9SNbU?)6$Vnnxj$wE%Z7`Eq*kGVH^ekL%w$?L^S4 zI*;Qh8JH*olYpI#E!ZNo-W+ru_>Z*CsTZZtx5cr>%5>lKnCXlQm7P_in9F-bu9U7| zu#9{&X}lG-Qq=P6x$;L^`7-*Wbi=E8?E!go;(BvRG@2<7c? zYz}sHRWwv@<0khQ1yhAmdhD(rnG)LVS8v;G*f+3SHbm|%T=K(Z&^5h7vU7rqK~K&I zlA+t!#g8qQWuw`;8~Q3fPE#;`$04lkzNYqVuES1|K3-%l7kf#$#OYc*2io5m8@Pjx z{GBX9xBi_hk~fPMjYd5Y3;T4ko9X0Tes|%)Cym_zr(tdqBL5`62zef(q*L=+wjkWH zqM|fsA#ZQ&j_$G|LLgg8d}C)ZT6|kX_8D zfK0?TqW0C#9ocSSW;;8(Zm$WFxvr4q8-@?!_hE+O?J`>2CS1OZvn|alvd-vFhyic_%ZadU0v= zLO(6r0z^k4q3z!Hfwmzz&G3iPc9xaZJjTN~r zSb22qns2vw2EC7$PuItg3~I!4fq4lcb_UJDkP9LR3GZ$#Bcr68oY0zQfoif{FP|*E zDvHS%v>FVqRulXE4L&4iC{YZM|f4c>tfT)@^ z)K^ya$cdI`Fw4evq`CL#RdFBwRSdkdt&WTUA_mRMZ@l@F$q|3e0&-~-N=-_8G9^a_ zxpM)L=!eI+>N=5$V@!{39336KNggf5h4M&UG+-DL5Njs$n8GQoBM&eBn&$QqSPdPT znjadb*~k&nw54GUKUj4o-{jCG8_OG{$U?T~u5emyi<)SAt2B{f1tW)Y*BqzNS^( z)in5~iy>5`6Oe;UL}|ENwdWT*l2osI?!4xCC-uz^s-ds5fH?}Ju*DM0OCbJzsJxYFN_a&zb*nXn$MApmJ^l4w@9pycInw# zv~+dlN`OCe1xxPpRAT@U>^ki%gejLGKSV)J*+m0a|Mjgc{h|rOwu4=ZhG`jW11LZJ z$|89Z^%tb_sjI13j8`t4d_LM)8o(mucA-3AHQjL12*EJ!71|{swK*NFV7JtO8_lG3 zUya9Zj>aHoTl9A>q~!tc7T};|w-o8AmAv?^-#hH)DqqKC_|WQRh&T5u-ecxh=J^NF z=XyKx(<3Z*QqiFo${U;dN!u>vX1m>q&n(A*lAFXAbeGr6$jHc0NuxSH-K!vR4cO?C zaoqRSy9NiTyz;MRW_Wwo9^G#uWN{)TR6*s(6^p627KVj^sNw(23_P)vWXQ zg`PoJ+~u)}^47ND%73}a8V3O_at(3{&e^Gk>kyg_k+{vu(=r{D$SWi)oXF>jO3yFf z&N9%baS7ke0emG3O{&u4 z0>Y%A=IvL&3FGHe2t*6T!V7+BW)Akv1LPeA%`$&Ilya{`_{o3YscAX9vmg`2|KUM1 zy$Yq|-As~lj}sSGlPA=ttI1~UDJd_NTTD;)WH@7q5TGPyNbhSIZu$P*=tt5Q;lR7e zqY)>Zl^787ay*F<&KoX`i@Q6vyQ*y~>%dNZgFK`<>!~txb;Vfd8BrTUHzXSzC-p{w?{c(+ck z>vPn10P4^09=5G6v%HACX3<~5!Ag!egvn%>gm+$Eo>%liuz(LWl-k`!3*IMYOq*S; zvoTVPKo)t`|9TQV+Z{NOPFK*Q^LwMV+;9%1t{71$7D*372*Qi!lC*(dfQ@0d^3l$k z2wh%eE{s9j*0^AwXB1fcPn>5wphjT-5O2j)k)A8Vb4g%sRW>S1)pU-tZv_V6!;!QW z`WAFGswT^y*>W7?-Pf0KB$jzR)RP!%*T)NNQzM^|c9ip#JYtSv%Ke&^GoJWB#}BVC zS3OTnE|SK!pX9KrU>|Yv?fy!rT#vCmuT+j)D<{uLpYFXqPKSBBg{g*vt=Vq-dbmb* zYx$zS!i)GP-=(~~937{HJ%W2_pxSv0PJxm!PsT?_Q^X=scEgm&tO$A)v!UuNIH}n+ zC@4uye49Svft~naM#voTJYwU+7lEL=JkOOBfua@3OT=o_`Ga@_35CR1Z&E;hMu&uM z*18|9j23P|VFpl;Bu}K>?uT{E5fH^1*^bR7MOFX0hkr^1e2QH+m*;6Ukl4#C#xj6^ z%vEL%HWTxK{1cdbQoYQe>@-YlcvSodNy~VnwA1KyH+7BU*Uz7=pbw;3`wixw2ycdv zv0G89ZdJJ1QQrw!O;<-NAXT&>rf|5s)S+STXk~R(?gRzqr#XaV&M!0wLU$ma7-;iWRv%g1_8-M) zR9Tz=_7-&J98kdHBqn7udxEJ&g+)!LPv`uM(Y?yctDJ-ADu!M0S{^*VoY0!fV~QV8 z_akpNh)K8X;AjZt=j0g5?H`SK#x>u;ziO29KvlLRXspt4q}o{*ct2r{{$?iH_sJ@Xxu4Wmj4Ri4fI z=hp&0SZp6Bk`yeho5(xy@bM9rUyVo8M8wUO-#Bj>UlkSXDOF`><4fdquGTu`jI_e%@qF=R{52*QwfFL0XJd1E7b$zrV)iL{Nq@X6Tbl>U`9E zyDA&x#sRS$Q9~AfyA9VGJ}=bgBy{(I5Zl5^;P4is$O|jR6N~`*yglfTn5}*Pa|d-x!CUN zN#v-;cYCrHM0kVp*3Pya+E9>yii$G9iv;zVWlZdWzOFvq9L?Ydf}9SE+Y_Ew1~tz< zq{v>sb}cy}Zn3S@bbyc@m#1~E|>@M5@0l#4*w zMr6iB+RfNL#34Cu#j7AyHD65`kpkmSzxHuf6nRN+73h-&2 z^(n!+^BWnmsl5hU{q+T&{{D+VCR!g5c?wcF^-^Ai9L297CH$enWHk+P;Hzcsb>?QJ^s zj1uqX1ut!a=%-&#*w&q1yka9%5LF%6;=!Zfi7LpXhlOT#3p{<#_~M4nlHD<`t|xL? zo{}OoL}ota_Q?j;?WZ3QP*WxCa8HEfHPqgmKdX6jyC&u55zrw{7f0BU*H8-7rkl?` z^u)3k{G6JCzHn%~F9P!%8XiD_UY)sx6iZXy=kFuFj9N0poYn|F(j?tQ% zMF6MBdveG`DB#yMwdGOjnXgccyQSJiy;hL9h;=}3%>3w)R$}LCZqxp1jx9>SM|I6( zr!t=V02Aa|^QC_13&L(N^g!36HJKxEY^xG!H)K;`=yCoWI>VnivS;-3^~P>)caLI4 zi>9V(X;AV7zz%`rs02}Z&(-^^tX@Zky?qj)J(ug0U_X;s`}O)c^t;K@RC`OVb@2uy zG3R^Hf^NQ1`6Zcp^*8MWVi+IM#$W%A@IZ=&5c9vy1@=FrqyHft{l82)x_dm#`PVGq zS03&EV^A2{Tj_@}MZGI^JxHaMC(QQIBkf10(b38+xTuzvMNX5M9aj$wqih%@(qw>f z7Q#V~7Vi)cEG!-QcRlPlIiC9YGtIN{+qZJf8bUnE1lzS!o)#7gcBoan_YDj_jrO6Z z3LTLEBPNgAzVr?gtb5;Gy3+IR#luRQ0~-Saimj^92Ltpb~_CHqTW zEc>0?Q!}`dFJ5R7d6`jvU#%VOw^?f3S>o5e97+l6D>>PaXt(Wyt+~X0RsAQlbAGR0 zK@&3L>A`O&TMKWvDfIz3)3L%5#S^BJw>q{GK;ErZV?(Z#V>RncQbsnn(34s6lS5h7 z!q78)sA&4e`8aAB^&Xq5L5TlP)VQ}!W0Wc`Zs6}!xG&-E)KsX^1en-qGq&7Pb=A5O-QXhusu5x#Lb(kLA+1}pU>u3(K;X0yik05uv`a|4zF(zW9VHJJ=d7hDkGDA=1o$$wE zA5hfQt$+HYmbd8>xaL`ZChJMXLuKTYaPb%mfOKe0@5kxEwD95%K)E>GR#(F&i7y?x zY|jnsr*%rnkozgeM&oxUD*NxdypM>8uoJ96ds(P?XX=ie7!LAA(6NrOMQ#q6#SQSw z4BXeA%@ArpSN*6#yh+DQ8=9|dK3XoKdG98=S5Jmyy6q5%YMIU$WfZ^n_tl+|w4lW@fJx;DPJC{`^!*iV>+QO@WNPO(_udY=9bo zG*BS7x3>?cr;f#83az6gps5uL{D&OPtOENvP4?^ZoZgT+7c~^hA~!yMTZ%0-t%?!J zO0hRhQ|;Z_Z2l@POaaAhDIK6vQAu<3qA>l@0Ttg;cfqnT33@zphiA>Ev}uQk2lc8M zsyN10q(R`Ep{RUs2{#=@1*;G(rJoE^s5K)qLuL~g0tEKb8}jEf`5fJpW|d+_e!b2T zw!G6E=dAe8L2aLnf;^qvm!)uTWtDfa&~3yRCh2dKRq?Pam(@g8Jv187jAkj2V$dR# zYSKj)bN7EgRIX(d+bG$@I38O(`ynA;l3!MIPSL&}MUZA@zmKy_JW%N}^@A+2gHb)h zPTJ}w+NT@D#|35_Rd6QnJf;q%l!#)f4F}m{2SUf>^M9f&yF~l>bttAq3H!{hr!{9E^LkK?*7qHIHTt4+se(!vOv`_nuS@)1j9r)gnFmrBi}YVMzke(dXjVbe!%?-hcOwMj;38#t33YAX9As{i?Tv#cc4Sa7O}*j@$P9 zgqVhiSkcl zuA7~-9%wC=z8?#o8l@~*FN0bPjB2Y%HyEIT43EoUF)b&Day;^%1fmX0qQ35wpc~5* zXM;_KT_NNwKf-cwT8h*k+X6`AtgmRb@3l||H^xr7{AJU$r-m)Wbb=W zxTQ4exPYWxr8Z=wS^5{bD|9GO=hP}9lBL78l(-)@nd3F?Tu$F_wje@ba{aig4Q*>q z?o52>bhdp7xGbio#xj0}$bu+c1Xt^j8BP!U^cc^a7A?DyiYLW|2 zDrd#LtPV46^%eOuPiSwF&{v+wW$@ZI8D?cb(?OqyhFP_D`%=+>Vj#1 z3*nBIddW~p+>ip2s!69USQET{S*U;Bf``ZiU^@Cfv$b#w$X{-b74e%qDt9bQ+^$Y8l;#w{rVC zxhKuKq=+snhQER|WSCM=IA$5cu!d{UeEx#N~v)%1*O#8Ob+-V_fOTEw75VPY=flfd}a9s z@bshGzE8Z~`P!iIFL&0(R>lye%w~$Wx1}iGgQVb^GZFvX_HUL{_p9jOI1OPzLD40g zfEsfn&S z719SM+xxl))X_zOqHfJ0p7ZixNK5Z{F8x`5q@SK8?9gyQmc|#3!jD6gxSZAO6S?(= zM-R3xYFq*_T8sezd9PGE$<%3oHE36rZm<~r_!!eC5Mp|bp2kl$J|8^;X{c}@p@_u7 z7;)Ujq9B=sbwS%~GqCQt?3PR18W5e1$w3bb@RI#& zoVRA*-%8Dpj9;}M5If8!YhgjZQs+)ow4X>WpNehj#e|4X&HXXDyW)Cwdh%`q@LvVO zcM!3|rM5u*q|EL%A3=^(2#n6jXQ`0`=&>E?s-Y2n_pV;-$iIAVx11M3qvC4L8xqNz zt!2CZ{bU7uLw{oJB>)sPC0GD%WC{BB|A)Y=Um_S>Ud?PsCnmDLMiy`w`z>flA(CE{E-f zgrlQ_L+gkep+8ZY))VQ|yYkA6G&9a~yA9XpK*2IsFz)FEeMuvH!#|&=wZzm{Tzx2| zNc>utntyKLp=3(a3|=J170t9f4mqa}tQnsh#4hulUml7y<-h}#7}OPJ96e8Q5;nC_ zxysm)cG)rlF%SPZV5Yg|RjSWfJw``Jr{>FNZNBpjQl z?B&ZTllKc0LnoEl!%@>ASnwxzy7N&6G-?ysQ(-;~yjmK-7 znsV8F|Iq2UCMzrV^XDDdS%?o5@}o;Y3G}x|U}1_RzVj-^CA)-;zxYgcS!JfMz9*kZ z{zo2YqhX{C5GcL?g$sQ9c3(1|%aI!s<6B>IYpb7R@X$zsXe2{}M=7`zU_`&S>e|H- z{YP3%10wMsC~_e$1qXIf(vaQ#=AYRL*>HxlVvxe=>o1#r=X(do64f!~lJUw`n1mZU z@oA6m6N#Q{5mSBvdR-hm@hSET-Chhi&^?c9JO|T)#r~YVp4{2z=EYz^O&5*q?-}5; zUBqaoR?TP|C@=TAj@QYmU3oW~vZ7BiqDVZ#Ow!>CDN`Ec;$S--bVjIDtVlngn1>zu z%$HF)TfR`I0Xp8#@05|74?VlVx+@+mUK27~JY7Y?vzaBGe8Dv@s`10=`3{A(Wf%ZA zh(&bHSRX3YX}qn{a9kn2YsBmIXz-?K-+TmhH8HqTSXm|WCTc~*4 zJe4B66sWH_&DXuPtKp;-%dv~@M%l+1uWP1R-BR%q5|y`Nxs;TWNp8F>Y`E^fuh37r zHc=&|8>OajQ4vzhg8?OhMz#Z646eGUAbB3Z04PREyfTu>$f_zQS1d=_kb0GHv+MgWyBZv@-PDP#nHJg0?UxL|NqaZ`8%XTe;A zUwdB0=y1aQ3akgWF#A4=fF?sKfm^w99ga1;!a}FTW&}IuAh&mTa8R_x;d-%wEh;1= zWpG?!j~ihxZd@~Cve}od_)F*OdOF%rVNtr+r{{Q_DG}@K0F2b$i*np7tmM%}I_n>j zmBn#axa_~%r6^Ywk)P=+B!WZiGrpdMjb7H6#GIsnZt z4(ArB$K>5XHiP+|mH5QhV?-^cmL}@X>d~%W#v)0nT=@hVFB*r2Fid$Kz~ri?yrWM| z^VxGgen0zZV!Yt&)zUy+O?yDcPcf{Q%U?v5XXdt-njBHR*;L96hjM_qP+<`aM#rJS zKHx|Z?W5_QIM=N1`O(VZOEeB*Nj4eT%Ai<@XqE)YX;jTYrDMT3@DK5sKvezf;pO2X zEiF#N`m{2RO&4~Ox6&ypEGINBpYyl_O=2{Ww=ui4hT8?orK4{t3kEqFg)WdtRXMO$ zfdC70vHu+}>ti;pRQ2V_2;0{->FC!5Q|1{bJU#r>_X`3s-B5YMMNUpSHp86+s&_(U zpC}pr82E(s?zAs+4MuS=#z;%Om%tW;;=^xFS0^kh^x{u9*3mmOKTkq`?~Lo&@DS3r z=aM>6nyC(Sc0wMj8tfXB+V6Av`uY-tI9h#5NvZR~$GGGT&5I2DrWvuw!GO;2m&q@v zsu*HHiKe96$z(=MEB{?cSgGUN;J4wHY>5#zR+D3^x>wQB&XC=rmbKjrGz`sX52Kfg zg=1m{OE!NrnY$L7+^q3Sbi+^AZC9_Z6uqDdH6Vt(emA|IzJY9+Se=aOu}7%5zA`8!)z-D&UH#+W!1)E{x#&YNW0jme#*1*+U9v23 zR#6V=qDcQd*!z%csU)om_tvfZ$oTzlP79e~Sj*}p*jZ^Ui8T@=10>ef5rQgP!0zw% znu?9JQNS!>zF&1z~>PMtt}{)qViCaLYQU zsBnE_ee&_5GTfsqh}dD~1qAr#(&d@-f9xuhqpYxB==rp6xtGxS(crg}FZmHV8IAWAO z@qBZ&w5hpx;kWVe=kg!(2-|;LRa( z&hRZW)jwF#7BD$#vGF(cnTe%$R2I9f?svk_S1t=zL6if1u}L7VuHF&*hKy=;_F~yr z+5TWKR9TYUGfv(0NpN{Ph%w*&iL)IA7?`ts~b`E(;F4o~H}m>P(H2 zX}R(1EHa-Ke2BY{(2Xo^lICQ~+8aQgKfUscDSnMOe4#GX$Dw@Z-jDAGRV?ZS88raS z{rT!9t=Jt9eeyKbo#o=YZqKSES;x0rzEA@NP)ojPUeB>xF+YzsiBBAdH&2#!<2r7l z!w3}Dmr1UtTfvNU(?4)(c?d^6YH_pzXt%0En)g|dpPUHxLr}LC5Q+Q7scXPLFr>93 zOmjNK2mU)N7FH#L-Ui*}s6S;w+7iR)Bd89vKV7LlU+#ZdUb7k~nD{Z@^_6to zryK1nXfca2>2`?z6(t)h=`DHWm3TA7FC24=n>iodz`9O zh$`qSgJf+Ll)qw(i8K`c&4Jc0l#*H0AOj`}GeSu_{tjLxeKWH#e!L|)IRSwQ>Q;C< zh>b3o4emu-f*UXfX(CcZ=1ji6r}v&LBBWTr7&Exh=ufK|W`*_7#@5K!-KvPewG`&f zA4y3tvqPBr`Eb!U^0w*d>CkxuvXcFTokX`yvb)`H)t=W2s9pV5?fI#-EO0A4tngS} za5fwrd>rIP(!45&nHd8Fu}auMU=lHCRIe|lKfzs-Mdrgkph=1pP7oc5Jae)Gfytd`TN9ppFEkHPvp6S_W2GQ?o7X8#57)0b+@Yh- zyU?Kj?FkpxM78zLibPV@UlXWL$aJH}D(Rx(DQv*<|71GAfKvJLVqCOXYw|+HH>%dV z6>n1cs7av;^=xMn=%@fjdyKyLOQq*aSo%9`sb$j2QVe5ZnC3(4$7DQ(oUBJ^e%KX` zc{fNo??T4%WSG_S%(8=@W{GEd_@1Gw(%2SN|Kso-#oFe?C)RNn3LrI^BlFewM4837 z;iuVf5q(2%Z*H9WP}+1FCkh4k?GNJ`pwgT`k1uvJULMFbwml3LN&`uOz9WK}R1Zm) zB^e!;9=i4Ga;4wAnuX8^Qz+1Icep~0*+h<2a0_)jxqK(>k0(&J#@Ghj_!M#!!4hmF zF7AJ3>nWT`XhY5b!}}fOd=PWT&>IpCvIi3pSRaoDjNX66Ox|5HYc?PYiuYi12Q~w>dfEXu&ZhA~FK;EMTcwVHxwdSZkXxUZ?Rj^e_;BW<%#3vNQCAsFCCAf!k}t_qMC25h z3pY;>THA2W4ldWma#ZG&dQs0UVjL3^*9Jyc5O9rE3tq3iappZedjLYT&Y|^sw5@i! zHqpU|-T^$mfvn6NvPZ`bsOPsBc-#+s6z6rQ;~jupx7Cvwq}v#%Yk1>7jBs(1PZu~x z!WMeo@W08Bh<7;~uW<R8B*f4WI8h`>(vv$X4r&sZRHcdvm*A1SZXN2P)~orgim zPzQ-9Xio%td%7PUfPjV`LooQyXj%Qn&qkr=?*?+IJ?W9Z(^7LN^+CU!xK=rIfB(8f zY;=rE-9$%d7hb3DAdjxkKlqfDl{L-497qhbCl_E9Ihu`>8MJ3ZK&~_Vvt4s+0^FAz zL0JMrk@Sr87PDuDi~b9C${zC{z8InsVjt^Lv@1pQ0oWG~6bpz2Eb^!t>KHa`{30a^ zObm>+wr?X4kOP^I#v{-1Wo2eaq_@>Jjt>sLL)~f$Byitql+ceB*Vm>Qy0DTO9Zhi7 z6q^k4z=P?99?1C+^E!R9tUKLaz-msSN}3+)*j~ZKol3l!9_qB~bK%>@4c9B#_vJB7 z`to%j4hb(fc3pLV13%!Z;@KS;;vLH@2kuk7IR0Tp?jxEa-t|i$n2KjyfkDXf&U@%* zV<2GN6@o}=1}TLyo9}FmWJH=! zdtiUmP8E`OW`m@X!Or5Q!wfa|L&;AG46e*7F#OW&d3rV@)}Zn%k@Lxud0OtV#%PW> zG~w{#=hsFW{BXyrJyK=UTP}MG*~FTRS9A0Qh}>!FNMsMXV8U{$JtszT+?Gx094O9~ zC=9N_uP!6{jDO7n(*E`ie&6L7t$EG=Yvlj;U@F!PHY2DwURIs`b)^DLfB=Ti&Mz&* zMnyjojVV(TTNpu3TwJ>XSeTJYd$FzAsivuqU)kGEapjnqK?Gg2Ne+0b%%@M6%FL%X zGt_)W ztOam|Z5-57{QUfvk+MdcC+AV{pH+e#?C)fU4Q59J~O6X_XoZk z^f|OQpYIyW_012L+eL9YuEsIMR~1=WQg_;H<_y~9h13JPbXWJOkYrIHLtg@yqs5mu zUL0o&8@YGSBoE39b-iy8HG1FQTvqbHRi^>t#OAaJMDXe4Ki=H((MKc(9yEI)OG-*g zO%>(z;=GeEj^~kK`=mhpsn+kw=rbKSElKi}L2;cWS_pqF-agBlP-VF_c?(X8(VXU$ zEFMiQ3QBQ_r85ITYiDBit`KR&K3?fNVx*``lj~)s4|fkHB@Xw-AgMOpV|e|;bA|lj zKOBP6pPG4;!fAoY!@aR? z-@3s3%jYUo_LWRLNNOdce>MdUWc`f8>Y^2zWqnNZ8@zTeMJbNO?FkLG!SYzuTtTh- zncX&`Sz_+2XXQ~+u!m*WrAzxA@$8xmE)6ZtfBz?R?T;d{qAE@*b0G^@9ApS-=m-Y6 zLr+E*v?K;6eDrhZk2t@CR2`jQK|o|JTY*+rH>Ib$yUJlHqoAPUm`}P$FDrzc;QPgk z7vE;I$2|7*#|@QY)hxY38JMY`hI#kNhc0}eiR&bt6P1#hD6=;`aoGvyIe#fh2);*i zaRv9)tx1mSIBQKsJf?k`h2gJX;j(-A$n%+vO$fLRrRr*7wL<~Ej^1-YH|y8!Ra)6voOdUyfvrsd?A;AuiK=d;(T_thVNjQDV~ASVYL z%uUl(<>dt#83t`BsV+y6EAH}5|)n((Io zH@@NjKYXVC=dU>TTc!yw1PkF`-|GKkR^0}{D>vlcx4eKO21@eG$BVi~N$eI{OV7p# zng4uclZHw{ZtH?GG%PLz8Y&i|CY-4j7u;)(DDeocZvRR4JJ+cN52{TfG=EN(f-gp| zZnVeWthw;}Pv~?5E687O6AR($=RbcRFm3L9lSkmx>>(kuW{g7_)I-(Rgr?*Q(P_4QvfrD8`5G)aYs0a1>3=Z;*i zQ{ix_38ZP%RFC$)CAqtwoN_sp!wJaqq%T!EnIFSSL{ya9db3S8krK{|M#CkKQwi$p z>(98H=9dQcYu#PB;3VUL4A1FTE4PYX6gt?{rxeFGw6JLW{r*mK<)wYYzx-4pPMy%( zN4WDHjE|4{uH)j)(%>dM(q3F(HXJf0O>~7WusH8|9uHAt^r<`X}-(lbT2TUVJngV z^CmWSn@|D%2BwY(yT!PH2RmqKIpd=~o+|!+3C{SyjIsDIAofKDvjSaocsTh{W?tjOv z#~=AL{|9hR*`VZGq^o=J_0xuWn^Ysh9OeUBxow{QjQi*5=x6sHzI}7gRIy-i&LQb# z;e*sOk}t1r%VkPp$53$n@a664^L3fB{*!!~ z-}KiHszBZx{yy#@cz$F1t~^!4!=_iFa1&OoHM^FoZ8DHA zau&rm+oS?I;XnSVAFse+0@^$TMV^aGIp|u=6R#Uu*RpAEH;?+H+;`M6z8`bWa}>)_ zE4k$l@ae0VKmKbEy092)n4rU8EU4H-$I zlKVa_Br1w1C;$TDGaGA8LD(N|Rl73M(W!0d3y6sLg0oHRO#asAlmb8hV{sziv!T{m zNNVcp=zvF+s_HF#qG-Agh_)0T-bTA}MOa9PX4OnZg)mf%l+VNE?DXPF@Ti;UZFe!9 zsg8;5#H}_;xSAIhYKEC35U+5h<4B2#w~j0LW;jBLO61nYy}J3b5!sPx-7@;bQu@$Xij;SIV-M?vq@=;`PY3poUzX#F_r8mJMXe2ZC8eJDFA}+( zUAm_iyuH08CHs(;BM)z0g@T>)O!mg)r1xadu!kjUtYPB_wJZqakFG-AJ?{4c^eY3C*x` z=<#NQ<1Bt0Bkv20b=;|V#eO>kSZqx6R-(JT2pelnac$?jFcyVbjjz6(m z`bM~Xl|$Z@?KM;In^NhSjkyF+f7z}HyXJYuvxzj6xdG|Fh3}K807U``MGGp~A9hc( z$IiuYQERL~pJ^-9%?!NDBQz;Lbnw5Jd+VquyEbkVP%#h?C6rVU99mjhLAqN>=^T)5 zP$>bC?(XicK|s3Op+$!7p$C|6`{?t&?^@@-v%YoK;Xl^Q+;h*q_rCVEuj?0J-2xM( z{vT}KQ)kz9v;J92AFu!@T#+n%ExvJ~8Z45b#)| zX~YPb*maw;nJGcWk9C1BDX41&$28C#-I;TK>0}};-3tV|*B0U{S7qho;!^9Kwt?HC zeTJb|-N|daA*JpjIFeNd)?k5PH=Po*`L`CZZqq3xCMH(-pcc%fy+s1tX@J@1g`RJ% zt=S;yE86Ar>aG=v_}N)u1dqxR1OdB84sA$Xr+Dep!YI>c09KW~K3nY`s*SNZg^Xc; zzf~ti*c+ZK_2_E3hvQu0ql`HO*%7S4i~r6qz`GF|L~Ve=Ctuok1Oh_OoXJSEJa#Cj zRtMup!1mJ-vMhkS-LbXSLYT(ud_~=n4#{|S351%%L)v`({Lm*0Kfn8a2g-UqwEk^U zGO@*Or)xbejkRprEZTJf&huCuiA^G+3pVhIUwc0d0olxIG%I{df-ua$rsWQP=#M({ zneOm*`jk`m%C%hQcS>~)pQaJ01r7_~?=B!?IiA~U7l31v!CSu{1Ndqh$G7wI$4q*~-WZW20$}Nq z#~n2kxVu=LF342_DN`DPxM?pr|*dIn+#`~lKV@Zd(&12wU_-?Nps>N-_r0(9DA-lT_D&GA zN37K7@{ph}DGg%)R41YYeI=2r{W^74XBHiycF|`Qj*2XqMpPrneOGq(K`i6IbjT_Y z&L>(`IS>zhRJueUc5b$uS6=$`6=(cYPqaahPJv4j$I#oSGHfxg^X;H>x@>N=Vj-3* zMfG-#vc+lW59tg}F*+t^L0qWWE5kqG>bLQCggZgwhQuvIH*S`3yVmG>hFEYF^p|FV zsuY~o`~tnM5;r?J**hEiJp-7&TIg2hL}vTlXg@l#l*N!V33>-m78W1T+R{3zrCSy?_Kaug+unW5#EI+#O(s(s4A))RCzeb^Y z$M7ENtKvP2!KAZZSZvRL<&}MknDG2Z+LmNmG!vl0#a_WHjFGg#tEJlIRgRl674&*l zr&Mr@y>q#+EkhqwDm*GD_rvbZ^Xk5F3((SFyzK7V6#65p4W!y%g&665FWI7_$!DS? z5&kBz|LJH`ee7Ps<=EzYbITPe7!?K{fI#Gn^>S>o=6g*s8GSc(vi<&2 zv|8a5;A+dmr$%mhuN1kCChCDLxZY-$Wqr+adb|g)^;3lmxlyar)tfWbmO7GjPBf_g zSS#sUe!`t!$!JNQMMU)Z3V9#pytu{hxqXh?5l|zaEJTyMGf~*M6uf6ot>Nm92Fj;z_Vk4;Md!=Xyg_)@)-X-gx0-0AeIe} zXx#q}U9dLy^&2nLsx^^&ww@XuK2m{Gs$DIA9+earxWBH_Wt6Fr1=X4|lt4!|C%?w? z3!xNn`7S?Z036A~wc@ampwFY9`{4e4pc>1JqBnhBA`>f5xfA=9ocCp*sh$5Qy{FRD zT;s{I*8nr;Ve{d-a1htw2HeM<<_^!hOXYd%xf&flsI|4z@z!m4)5O?VBDWi1)grlRRIYWF|9OI8l%@oNI^%0uL45%I_t_4yE_8`QEzrQ4`fbQIs11 zDHX@*)?q+nJ1RVsa(Q)H05VGlK03OU@FD!6Y~{-=#i{bln+ho}*j*I8|4P_{t&9c= zP3b>WD9G4w`S_dl4KnhW#J9}+Ua#4Gg_|6D7KC5&U7qCqR9BA)ngLbEu&rxucJtk@ zsoWfP4fOR{YB9v5&W|oPQ3tOI3?NZNIK=%rwNA#MZSpXBSv||;)f7wekM*i5PC%xC zD*!$^W3u&VmTfq-U$*zv=_y&Jh&7TM0(s3R2;ImM(H^?nE0lS{*cn1G@HV*fD+To3 zyT_L*HDz(yCa?`tX5)c4HnpAbH*oFyS8+0|dS2eeYQe-nwie1%%N3-*J=5q8AMF5B zOtZynB2-E_K|~CU&|5n=txg0tzsvB;4n!iCB||d}tAGGC*s79iDp4uTghC*$yiBY~ zjwyX(|Fg|FsHy_FIi62TM)nWI^#gi!@79yN5D*M1Z0-!*CfYCZ$K7CL8T#cNN|%-G z={@3TWV8%i0<*ojEhoPyWxNht8a1zPbTZN-b2tk^q425by`oZ5QW6-hN{y)1ja(7!8;~G5HBKI z0xXT~0}0S|KP}fCGX0MpvH)`%Z$!>GA%Zv9h%wnJ8Ys|y`Fwh;g*ook6_WUU>Srz} zbY|z~gr6&f5PJ9-?hjPD-0P3;gym3L@jm?M6)VyNtPmc_vbbJ&Xc{5TbWv%FIeAJT+j5~ zx-_txH(uZnF_UDam|%QBm~}`{96<}^ud?Y|{<1V?gXoJb)M=zc0 z-cy@A#=K~|F3R)c^QSv>PQfA7PTLJm&ZTpk({uGjb2>l#-uZlje5PiQh-3a-A~w^ovK-Orl+33m`vnJFQA$`zPOl~yyl}@fyu<~KIs6=w?RNDfXyZYb$_qZ zJ+|}Gx0N|E#AvyFu0PB3Y8P0=b#rh&2ejRx)*Nvws+xKvrWGHRn? zP@4F9yvY2D^769`gq)n58d?>1p36*8c=cqga7kOyHC5iuuA-|$X3L>ws@8co$7x1s zu>NYJLd~=5EuOn#i1+2=f-^aVK4O9MjJBclG*w}CcFs}l$L{pTjo11*SH>cv^~UeS z4N)}C_N9NWP@<=@Ok}+xkWz_m+|FZ>OXTZ89#@N@nbzi;W#H^z6a2i_M-k>@rtE)m z4G^jiVt}+bXr=sEUtHAaR>7&8NWt+r@D8b|sj(o-y5}^MGPfoyzAw9!*#)B{VWE8C@`9OJ zE`Fga)U!}E|4*Ic8=B{O*<;px>@rFLk0?=N;N5GaDe)`m74gK3$I}P}wqRiF zs>Jm2%*RuMMQ?=o+a1?pu8PIAQ}b_nnQligPdB#dxn}p&&eP*#+Gu-Kq=8|i{apmI zb_Vq59Jfc!`z9&AEY;qkp{8!yomY_Tc5`)_;V#A@QpTqS0q%HiSx>bFz=c5kFtEkS z{5+-jGy^)Lo=^yg3LJV8v~eP@8a{VR4#s{5M36!uNWuB}`<1>s!diDB0iQ-k?zd(@ zLfJpihD%0aGD$>#7}IeSDosmDX?d5vg0!vL{MDuKe4)JseU-}_nZd1+JD@u74qkx= zf3SDMFIDuF=dlnM9m;){HbNnkMZI(<1Mm_S&%$ZCcr|j>pPOu5Psmd*KFGcF>M5o2 z&>=96E$)qWb~1Q&|3M^B2rPmoYn19bZ;g8bzH1e$(_ni*sA+#(VR1Ha3Dl~-+b-<; zCu7?4#>ZspmSNW7RDsR#Gw?M?3<*;S9w_O2@P{SVT5W)8xXo?9}WLN=+G?0Ql~Gyn}{2&##W2A0?vKCSq%P#HAp#z>M>B zyLPbGIWm&FcrNd~>J$iF=vOz?fGO|1_f^S-ecSgI^%Bj@i+xyBR1^*g3m!RVlfuGB zLQDtSd@Do4zWA)6oQ=&O$!N`JGCqf}aHUBd4Umi`@H=CoUZ>xuKsQOHEPprF$0BOQZ3A9om znF5?xxwo+7@`N-`-vUk=pXUm>yM&b4+6>U+k73lxv8g6^{I?ch1+M@U`;Q-6y{>?< zi-D6=!a|w8=Y(T6!@82^{+TIauXBF^y*4<{ek_e3LHOfQ^w+;PF_E{8x1pc0n9y!; z-nCo&rhlvuv=j1(BV4KJn-;Qm6*gC_m2yZ$J32Y(Zy?xv0x37ktu&F-&_V>?(W{1x z_exdIR#A|^Hi|9adimU57eB^whv+R)1!|jX!oZiK|I6J8x^KVs>EuXbJzMndeKUX+ zd{)gj9+;eT8qK;*=xcxms#GnPH}R$l)G~p3e&Fpuh07G|n=R`88QA!wdD=kSYp@ES z)d|oIyE$k;ZZMAqW4ac6w@ui$QaCeTbsir7r_P9W2MWcMWI|SZV8y6W&En)fDeBbW z<+4y!fdL=qtLdCs#Sn_Tkh308Dgcw?5ygW{K8_R6WoA1-oBq6L6%~ijU||mF4SQPz zD@hu(`%hSKsM&D;+=rz}t=#)&X9=I}9aZK!R)RIz`|x3kx?q5rz~PoNNjiX8OjeyBDg!NkIquJKiY5{Ai9aSR_I|vQeIIp zTRGFLb+Q2GuUT4JS_gnW?*St8X{IS2YxJMG6q@RHrzQ?q8J^kan#C1B%Lc$RP$2bw zhql(zB6GrH&c7hymMhT5%3h)kV#oy{bV7((t@rA}PX#bC4eEHhcgIlp6I8on69~zI zLQCM1C_h=O5-^hSC9{2Aa3@=)Ftk&RqvTod8GVfv3Y>8o$`m16qt%rgp}R`w;)vdukz;}z#z3s;lhK6YyK;916=cT<6FQC5NOR?}IxO~oPiYHU(b zfswhpv-@tB3{Oud7&Yt6MyQQ@UpQ@ye-!_fRC)q*^Y+&{Rsk+RTf0EDBxWWveKvj3XaVptq|8&FMvqFVpM!>L;5hQ^s229rQLH z9$rp@!c1;F9$q#mob$bZ=iN)P8e@JnNTCG?L%`2ws~Lr#R}KKgN-|8Ubz!3G${?}$ z>R$R!@O~-vjDFwJyPa||pr^Zg_*lhy+Q`IYWlvH^x5ro+9>!H*1-$klo*# zFeW3||5;gj-yOA6Y&Xr_Me-aWyIrjOzgI>bdOLzEQ{4D7$rn+tFmM)0fVpkQGli6j zu}q23@nf$HDrve%E}Idj;WT~T7SzQ+q5@`7hFH+^`W#|vB~J?eGR4>bjKp5wOYm`x z$fP6z?^fY0hkWJZ=Bu^VC7)Ra4<8_J6J0vXqe{y{cvwuEW zX*ba@JX|!BaFrmywo2lTR=hm#)oZG@IBFP3thHk)vmKj^i+kfV4crW8W?;PQMrb+l z>eXK#UJ?8=EZ?RGlwK6|nY&j*#Hb-pqaqU^fh4B0S=?AScU( zXq0S4(Bi}k7#kUJdS4|0u%(fak;{C&D0)N|Kz{~!8WZClf(A#x*?kw8Q`7X%|6FD< z_9K58h8IDj)AsuG_6hK|6enn&u5qOC3yNzx)@tMiaACE}R{nvt==AiT!Z;*~cZ1LF ze;)K84*FAz_Id>J)QdYcRIE81XP8X3I+stl3O@PN(qmmaEV^yJR>eIKZ1W4?cR z6!5EcIUgUXfoieoNj@FbkXPAb#Me8Dw)xNPq(??Q9Kd*qxr#t+yLJ^C_#FWI9NFxdZK$J%)wE~Ziff5epdlXq?-6w^2Del0Mz?^VXz7NVB$ik|M_{RTmS<6 z_gP|`k%GGU-+w-M{Ex&}_Y^1~|NroX)4MNIdO^A%BQww)X55{{Iq>HuUtXmF9DUy} zq=T!7Ab}hQhU?AW$#BGB)%duP=QTr0h14Mu;F|mw^w3Sk&Tgcf|C%n6j_!NlA?^(< z;3O#F5OF{A(TAqTQVO|V!#}=%`e&wMe7KNf`uY=j_F4axZYzJ;V;jlQv40T~5QwZS z({in$)F}8I5KmkF*x%m|zVY>+fP-=89_jb&oTon#9bwOaHJCtHOiY2zf4vVgN^O!- zVtO$y9De6G-C;d00)MM37&q>O4h?mOv0(^Pc=_Escq#55H6-yDZ;jaU5&%VhUP^H5(Hol`uVdUPa82Xq|YuLOCH#csP^a10O-n(7b3^+eM7_LM%o-{Xf|1#hFMd_+l1`SA49Svbs?L~rW7S?F(>8n3L) zsS?_!PnYd}HWkNQd3m+Awk9BfD3zei0)T{P*APYb3<_WN*Vd+mg-wA&`}3BdB$TLp zinD-y9mC4*E=f_dG{mz;8l6_!G>d{IKQ;BXL#~9>)xBBi8#(K&F4UMXvE$|$)LHhp z-qO%AfE2~XP*nU4!0*iOJMlI-T|p%7c|}fUXUkA$cBJaW$(!s5<4)UOh)Y|z0U0Vh z8EhCH(u=U$BU<9B)$3!rY-?;vijdoQH!!Iwe%tIe2f77QrQQd={L*Vbf9@}~dafjT zmTu?WzT;;54vo2jmQqzcoMcX{_S`^^@2-yx-Sj|8zc%u1M78wupuWPhwv&WB{r?@6 zJe{N$UP))+pbtm^ly_K224^hRZAaRHdBe0UsYoa}+reaUqIte%3v1Lr1H)631~d(r zbSj<-2nf*7_?*!$&r(@P%g7*Kct4?~6`R2V^-zrxU45a|O11Nwo(ROevz5d2d8DDl z*K=X_PLKN2Iw1JLT`;h0Wu4G?xQ%Wx{sv`bF_oxWM+z{=49pg`AfvozxB=e^K$j|BVaaGXg;`?eLk(HfD78&YUs*gPB%~5?KN+%~LMR3ax6t~Xie^1}J z_scxb)-Ha;golTZMb4cSXj)7*oSwe$78b_AK?-@I0EWXtONk548@n>)_(kt$q1HW} zD#?4#T-;rq*GDIM;PGzwIg4Xk_`D=4f_$LMZ@)4);96PF8^x=!zIzpYLPWI1&)qlHLu0%1Dh7H}I# z`iN`!!kM(eXroNtvw<_`M(4mxd{|P0>BJA{O%|4EmoFLb&Z(PmkvAJBSy;n+rw`HY z^L3O8x=s7;1a2l>4in7ys7zxKg86v?(dvPm|(IO)Ebv^0rLu2>G>U@6aYm%Xxm^7v~!66D+YMsY6JUMz~ zgO2oWT#h}~wg$cRmZ2j>ufx-Kl=Gq{^2RPUV3Pd!#op zbB?3vg0IzF4fEkZ;(aG4pMbj~4>sgdIF%gvt1|3zZOncaYh_Z?B=^PQyQ94}s@8$z zwnN}i3wm~UuG#Y4yWwiBT7df+iwF#~OShAC(J|2b0%#5wpyUBUX&DgetxdPNf%OWo zTFC0TGZe0+2=W81d`U@3pmj9ky!M!IBi}mU0Oq}`YbzqsAx#8|cXYstRt0#pv;(%1 z#Ov2@-e}^Ha@IMlkE&h*Yd%}xK6N*~f1o(p($JM-MEeWJJ~$Y;0<$DOE-H4Hvgqm~ zV4*OFzMLAJu6VtFANYZ%@OZbvn%#lQ)|_T_1{SZw-Zvo5%T!!s1%*P3pbaN$tq@As zi>$i3ZPj^R1_rz7sz*t@ziEFUq=H}7XgxQrlqo(&{NwuPj{Qp0!GbsWqs{KlU;)uk z)1;oBPv*Ui9PNjcgjiC(uv_{b2;c?IF=*liQY+;r6XfElJxP%#l|CdxHz@t<1;A#(b}rtJ*=OTlQTW~ za_S6i?;QH|D-bql`|$A^Fa@-)4+aXj69qcKSdfn&UsYR7Y)yG%AT%m98&r!6Cf}2j z_bpd=NM^kF7y(#Bf|`YwHZAaIrg#)*SLb)&YtcVHKfgJcOf43y%*djd1z|p08_D!U z9ZS=W0<*+inbAt8?`qrCYJE2O7vS_ea15Ohe(l>>ry6p)_c5sO_3g&D9Hxp>4%-`Q z9~DbYhf;uJXBKUHTbq;B0@!z9K{6BFpKtXBZ#YdB>yVLO1QjqMnnAS;Old^FA3MC4 z2=r5lFOfqFI3%No6hkv7yTT8-+#Ezcy(u>AuiuPg^Gt}0tgNjiiB>R|=t8azDr)fB zEt?fs376au^gNHc^HiEeB(^J2$Z!SaygM=d0=f;%9S$kx%Jeg>=NiWrQxnc;LxZu> z7mqeIfPrDg;4>EP8qt8S0En+K5U%O0Z)j+Lx|d$4QS#>P+h`U-En> zz#C(^FdfN{d{)iXuvfMRB)OQhV4t{QrXZe7*XEY$oHOL=IIj)ALjGXWJbyqtV4!iy z84w{WFRckPs8@V^VcAPKu+kZ*u0;82pr#bQV8Q)h;EH**k`Ne-`$*hM|CaWo{6p%t z-K6J28qth=jk)sWVnbj_HREt#sieX?BZvh;^XNP68QFt2Y6C&*v%`%apO;j;OZnM7 zjbfhxU)g0dRBP~;r5KiT=hlYy+TEM|58-0=p<7tJIH86pbPZ6+Ixr5hrwb)s*%#8u zkCZej6`@fdpe%D5{IJ@(ODW{}wtKpN^T$$5h&Ji?doK^C8)BuSqqK%AP@6X@SF0Ou zFKc&N;u7e7YR9&z;mb2u*F2}>Nh=2z-h5%Hs^GhQ1LF9cnbJ@hNkzW$6ta>GY zRovMh?}3>*TFSiZP(bnMQLB7cSC`7SU^)Soq}qy#`8sosZ8r7&PTYnp05z*vn09_s zQ8=kEu%-f~%ts{-un znJ0OK^X|eUGBV)zd(a(D1FWxCir%Kw+62IGC!7|of%WgoVSY*rdf$C$!MwlUIsk9@ z(-|^|h`Gk%h}6pMuxm(lyGQLL;^V#Ybx+{GhcvRT1*y+A*4hpNJ%LK%Dgwm*ZX2votaeV z0;U>=)6C8)5J0V5uiC9TJ-u~9tN1ToP==i{TvZ-TOUlfqlcNpf6Gu|?Dk;(7$ z1sL?I2Hy?&LMNMKW@S`mhX`Vo=I~R}74VjSfLujGO)Q+wmT1NIO&)DPNIA`(ThIQe z0;j^z(7pHS?$C(?Ee3C3slFtJ{y|)kEZ`bc41J=frx%#f2-F)7Pmq9>tQ?sW0z6f| zLw4HURG5VW(?mUQxpY2|B*#tXKs?3@HCTg80Q%9q%)nzX9>=&MKJ-%7&5iFy#}8g$ zNkmWI^Cg2{ZTq494&RSoGOg!8st~*7P7Wr=jd0p^Wl|Xq;ffn-#hQ+D=eWeg#2hC5 zvvsx{#d(vR03z$S|EUXzGy$^c_Uf+l#VMO>w50qo@}v~>0f7ExK1DsZT{dLm<2BU8 z{L8Ew`UYaV^^C4m?<;tw>ClJOAp=7Lv-k?*J|L177OqEM|Ndb4c0$@`C}3o(?uPg1 zJ10r#RSIV@0B=S91bB|l409rnB_+k3Cgr@SOYHtLe%jTQ7*UcexLq}uAT5+$-?QeE z(rI9?@3z#%Z0Lg9r9Y-}eLXm%IEd-T{MI-cSlt?}PH9 zhl2(h9d9F4yQ@y%m8L|j>K;p3Qg=1%Rp$I6HVDS8P!^ZWrW_+%3wA;v$;99SJ>@%h z16Aqn8R5Kh6~tRcx*)qwo^!BORd6~9+f95|OkF`=Y57>w#5UuE^17bs5@;cbj~I7_ zoRU`-PHs1NvOcD!o8Rq7%zMb@*I8?jLn@)L>CWXnqR4v$6vF1|?iOxt%61!SrMbnV^~)x6 zSaK08E$$iKEna*Ax+db-$xIa|j3MM~76;3LhpQPKGMu~b42I~n8tThy&leh2|V^A$F?5lqP>IVA}^64WBXM{>+h0mYxb(k2+uQc4iMU zr3FbAuXih!J&yf(saG?0t@Q$fRtb4>&QKg%JN&H|;{Yh_1Fmp%PlmU2ECUeP4Ll1V9P|_C22e>< z7R(2W%#LC8`#i~W>kHqzHwFpSOH_O2=H^a%BKfK})k~;D-ABc&$Sf^~_-oDMNz&2g9k( zp^J-ZM3l6jKfF()2%;-6Gxv4tTeV@Xyo1M?hu2ie9MdwBVTOTOSeol%Qc^PTy&kKx z{q1-NM8eSL-K4<8dGepW&Ri5OLlSf^v19KHng zpJM`~co!fTj{*M-T5PIq z+rPP4YisHErR&l}jwDg{NrN`Yx?8OC@Dw=oUje@C3_dL}ao91`SDeX9U}zZ{<@-|-8O80|~neCIsZR!pnb4IrP~7UQ4kq<=)G zrSqFkop@@6S(HpnpAL}mB-C29&KFDqTw-(MiNnq;Er&`MEXs3Rvl@U2>J;@i_8}M2 zmOVzw8<U_yyji|$^Z2B%VXP#;Km=rJ*eu&g5V}Q-a3O@6p8kTD91B_0<4E8k$a9>W`&Po5A|w^@CLJbKGN`f#DZ&HwW=L$hU(P;p^k{;q;emDN?esQ4n3}wxBOTMpQyeub(w?TCaNFRRl#p_D4>u z@eNc%W{$3W@#~KqGR&>5#YGLO=hzN7^v?#(o`X7k&bM#h0`MsTcgSOGWdLwVmGhqp z$rl}`ua}Qtn+M#3Uv4{>-xGfkZGGxmIGOa((RAy-R9)U zKYu7xbIJabOSIJ2^Ew^?DTWP{0XvYb5~5Q}x$hY+ zMB!lE)dv5>uG&yXZkMhdv~B9b5FnNGc#xrFi^6bF=cX2A?{={2d=1h^Q}I~aG;fPV zi)^UBJ1gkWcB(0qS3|CV*$SiX-&(-u5fP}0LzaxXmwl$Y<(t(Otm(rOUB ztE*-dq4)Z9b>neFPYpj*LNBB`gqbKmKA)aQRd@p&v*FW)0(^uthPA0<&Ou9ME820X z+n>gXSFGHqR?XxB?HpI^iHZPNVF$gFl@bKQbU(gGKGV)D1nLGrM}&r2=x}?=`)t%0 zsLd5AWzrcZ{j(aSCR2kRmY9?m=A>3WHD~`@!IqoQkSy6i^$hAt}VfxDalq@l||2oKGXhDD=8R@Bru7>tboJeKp^Fy`-8! zam)OBKQ!^!tzWl3l^A*3Wq<|kjyl&m*7YDGp2&pioLA~PJ_}FT`>0nIogFw-8AcJX z+;Y&KQuNSVnAs%tp3+xQ-R6+H9+y|5s!b~q_^H?}UL;}!O=Fi_K+WHzuo`+?>{Tp|+R=RD5r zCI}Iu59NHCW`3N(89fi0THvIkfG*apj=*IlB_r$V>;%qvqb?Di(8`14E(f7YcF5;J z39$d~*R2r{5WEVGhA6eQ^?RV_J*zH&NS@%J>7{PthEr5eS#>qDa@8745Vcl?0v85y zE}Wk;#(Jk6EvSSX;>1@zeq;rLXm7P_H(zaQI{vn@7I?e}6qH$g^k81mj>d6<%Af!Q z4ntVbz1m|62;|8S_pAefCJ_igxfXRbYS9N{(V7j$wu%1veWSIKfx6Bg$ zNm2?w%z!F{{uYG-77&YTijDHWMWIfD;uC^L;K*)*cIVycY^SX+AtB0ZpX@9wB)cue z32M2NZ|g!NFo3sLox{klz*3v;&8bjIp?=j_ZskI!ZBtClsZt$$d>o(_rI}+8_AP$w z%iZ`VQ+>te7)7&wTZX3*$(h^VlpGt}5O=bd!44aMM`@&yzL()+xSqBAR9ZQn-5Bm} z#2Jb;kR$}1PO7&bj|jgMk0|BsLx$`0q1}gc8;(kT_&a zJHA7Eg-fae91E*-rF8yA+06z3*k;x3-n>9PpI#BVqgILY@3BR=dIw-;>KhyT_>*u* zA|fJmTLvd@E8-eOYUaI9&loS?nv*!XILMX(5GC^%1(W02bo&*N$^<2;QI|MsRxgEA z-A1=)X6EXzY^x$;oKX>L)9CertoQ(vwA3LF)LkUV8TT>i$xv5Zq-ReZUky%@5pk2L?H))88M*S+j~(Q1er0P0CA3 zuMcO$#0L^wnp+w-82=njo6@H-v38}ru>P4P?29F&Q^di=UG{wJoJP?f+pS79uuXj{ zHS@d*X73O#r}#f35>Vpq&?@Mv+g638lLYM`3! z{p0WEr+u~p4;t!ym}Eof(8z@mdtDk-l@rt37Xgka2PaSBhvy67mKj)HO7aQ{d8Mr9 zl~YvB=&gxbgU6RjiRCVbKM_iy=n=+>YPATd9emjyhhRr$ba zqLP@uzeK(Hyj>zxJe0KAW++A1`*;OOe~WHyrUeZuo7lMIF2I&BhPgseL^(?`e8jMm zQrO9A`jEbyjSaNSLHPyHoG65y9J&`h0=#ZW$oJjk;yuzG_ z7!suK1n@uXYv=g0-tTq+Oc$)kO3D#LZ+=*fp+Ux5d&rKtez+sr5L9{1bb z-Iyn2Y#t9MZi;H+3MAqB4+C(-oc6QB0> zHo!ef?b9icjA8?NI7s1jms+tUIzSN7{_3J0oDUNq^ZTWfY%8;l5b4q3AKj9+!xZpIqcoexs^s6PMn0)3 zWpZEr0LPy?%U_+U@Rk0A%c*542Z{`Pk~1y(qeHQ+&CUU)eu};*eR~N+(YZT)LSg|> z9RzyiJT|j+K+)V{qHu(AjHr~I? z^rDWf(sY2~ZN*bDSI)0^3qb#~8Rd^mQ&D468_)QkxEJ+8B_pcuj<{&^lp{uz6! zr31FV|M*AewI50_10*r+))qJhS5xp0Uio%!mQxqy{Em35=Wd8fz<=@C5PpZ@Hw2l_ zg3qctr)Rlc=beFRdKdn4R4 zmM9gD$ASnjc_4Zbk#uNdW8)5w`n5bW1VrR5o~>>~|R5&2ekb4IkKJFRqH5;I=&G zlSynjRt$)#hOxPIbeB?7yB+mqxK+6RqB|yN0lV~cY{is@D@{L7%36SL6N|=R{ReU2 zNxLu>Uuoifgptz50#HX8_vIz;{jS>>WJ_~$2(fqp)gXJPqzP?D|LzX|*I|#; z)8n0)li&;vroo%ZFcCXDcA{yZwq0$!vB{2VjyQ2dM4|(R7v7xVG&Bjm{9JbN0-{Cx z8yj+mrRh6`*DIBnE#ke?N(pB}jHr;B`?oD%yar{NVosQ&{Sh|@GE^C z5dZ7+@F8Z_vYxK{`F-CIxWP{ps5#q32vZtpMEnS);$fkuhYt>x01ZspD|H%FXcQvi zf(zNSi=49n9mOnjm6gU(eVEn(ti1uVbcc#u7xMZi9}CVWWH_T@`|m~;4X2lU~G z*}%|H4lXXkMfXU_ry$|<_ZKe!M0}2prBl7SZ{R+GN79ej^<~kKkr7a@tRwkN2!h=J zpnL(q30nS`x`)T~2W@gn@Ei2UiRmGe_y!LyYA;(y(K72 z$MR|hOJc|!H?AoqS+`~LBBz=1KbVqhx8Aj)gTc+R>NY=kmSdr{sJ0eB=6ne^7gC5F zYA<#8`(?{G095mlVDF$*=9&Wfn__jmulj9ID%$W~GtIM^ z{YQ`DS#?|RJt$)0|&t|YD(w5?ycY6P_u8?S}%M?SQG~@t+rItTwGkAsnpn3h?Vri zx@xi&Y^=$b;b^D%g{?4i`t-4H&VXDW${ib+?IRr{agj0M$g=Ln>&h zWzIk27wOjV>?zM0ceDINg)^m4?pZS*cY-kNS>~-&FjPAeGq*5PO-&5emf!Sy!p;65 z1u-zZFn&u-UnA40sNkG$FnbZ&h2wGQg^CClv+55`Y8FMN-fT&%u^Reuwe2xfOnzlr zm@nhp;zElrKcMBg2z_(d;Oc`{lDM|4_$7S7khZ1AVm{&8ee=%kb{y8N1O#g9s6DrJ zOV>eY{u6DuG&}&;nElLC>$ViV1h@RoLiPI3$`s{}q%%vrPTHq7Z=M?s71*9rr>iEk z{vE(?pe^=*?YhpeGpnclSb^Qmb+eGg@w86A1`_%9%B@n+jM zvOCxlpCeT13~i#72dzei{f4G4E%$f$@lzFanq;z`S28L%14>hvQEO)JO_{I?cB zTq4|L(Uotk70OsUT%k{t`r)3~cWKkEbjDXteo_hd1ib?Q$h@6cT&vqI2glS(0?ccZ zdA%dZd9>Awipk3@#0-!t`}244{CC|QTPW2ONPcGI5y{n>EiNr9M$uU@%l%qHgx({r zVM95u=fyJ~U-5c5K0;BWS;*A7r@ySAM_?b(!8|C1xso@7C5FM%0fTO&8#v}yb(q@` zDr2!w!PeDv2Q#HY_Zc_NvHtO4SRHO7zr9NPeLrS%5y&#Y?2LGGLN)q3+xmYf>V@Xy zhOB|65o~vvIUvH%eD}Y1$R`yza`)R%^y9D9Wla4S7@#;?uv>m^tLrBY$<*=5wdUl! zSDXY7{qs0aCpumSJW7n?zz_z8t-gA_LxH;CYWxaRyw1;bsE+{X1oi5r52^fruK(~i zo9$a@A_zJYD}C*#YK~W|T+vUf1(KA60z$xnqPgsvV#t6#z=e zRrK+_0~f6C|6ceyDQy1?i?o*w!xeKEe4OUqorh8;vJP=D7+a{qb(bj8e=hSF-Q67~ zm3=>6HX$PE@hH#h!#f9I^2!FB*TS6H0jz&aIbSPFC_LxUpD*pE0@i*m0MtJTeT7m$3g4viiUJf(Q0v1!EJFoa}59qiz5; zO)&X$yN|tx!M=&ucWn>=1DOMGqu~6xOe^K4X<<f+Qa=2-@CPivu2oYo>A+lS5xM31m`1dBmF6$rRPo!e zqC8zBJ3z5wfw=^pp5uRh86o8FOaVE_pRWP+`Ts9r$EQcYqv&83UI~W!WvegPgC62c zQNdiNQqKaND4Mz79W;PA*Nnc0g!|dJpoX+O-%s{l0<>zq+p77X%-6W;=(BnVw9qLf zClJ7X!z*hM+7U`@OY)*5U7gR^g_VJ=8Yp1_JLs)c<^03xi9(CMr%#`ro(kmLj+b@1 zn&s8>%K`LM0uqwKhB#G*z@Q+Y0d^N3$CT&cg9nib5h7?m-zJl(^H(XS=RqC&{mvrm z_;)>8Jt{#3X+&wH{{#stZa`69Zu7e-B$O%fiWTye`vDKq^#V914XL-EGO#!xbT6Cm9V`sn+%`#=XkRclMsLC!c?|6SHW)M@=#(RC+JW>$m#@gE8}8 z{@9zZ*TwA>VxV(`L+4Weniys-V%B|^nOyi zCZJfnX)wBt8{=hmcf@n`YuR3rXj;+p7L38(QaK@}>jO>+WM>RCNfTERD$pi#b92g7 z)~u|o=L<~K>}_+GdMoZKg1XZ#+J~pSAG=d)GHv#=2jZ{axFMJH{0Ri2=-Rxh^^lB= z47jD3NKN2f1UJwOGCvoVwPtvN0W9JG<{k4%r1333tbsE&+uKXu$0It5+Nlkg2SUyS zbiei0%Hwr7I4rXzM2%Ms@2#=Z1`j(Vy`W##$1olQwIw08vo2Va_kY`qs6qa59 z^Xz-0%Zt{54Yh_{vT?`n6^%l#N~-$;Bsz;7g*nRH?NLkI7Cm_yb3HHhc6*a^4bN+f zm#h@*pDMp~0A?K7=EI54wPZL{46h|fvAsa2Dfafo7vftn$y%y9y<0w^HXdc?`|{OC z=Lt6)tcR%a!zHnz?}0ty*yn`*hqkv2i+by*R}6s@B4Z2cpU4MOBiN`|Nl4N@8|rSR%uf53e%pS zS=q4S67bT?^7+El=FjL~VBO(*e=t;6Z!|p<$O6IYtn_~jlZgcYPcGtB+uLq$ zO~T9uv#JR97r_-vxvb!5d1sb1c3U{_%ge7>Ss4#}1c}13D~dHE#9t}pDa85w@IE`c z*Kypyd2YPAlx5+JZ%pIor=T30Y+W{x`;0PKlUxOj$&4hoA! zl(w`7{T-M?e3@J9lP=RMI@Y7I0sP|ROEZLu!c@P2ap7NtLU_Xup!{Ppeu_vu?g(cx4W zIm08cEhAM+eh~^rtSV;$BDi5!gCA=mHyVV={#`a71i$~*{L-eBxXfgk=|;tETwDtt z(Qs1t@F~C3PgM9KH?!C-u9De{n!n;&ooo3dk1|54)dJ_APrc67<>ZdP#GtD;sTY4~ zdv?4vkLxNsk+Fi^_>Dt>@+*{=Ms}dcnU|2+(O<7s5OMP*mWl>I>5xmTs^40{F+yS=9v11U#}u+vvr1}<6#^@yh0vkt)*821kFWGPsk_#Z!N z$um2$?AMbH;}mcUk7g*Z_iAjykOy^GC2)g z6!h8wtQ8Tjo83hdtVFNb-63J4$MAUaU-1n~@HS{^nE(eeuC)tuICAlh^J0CR=6)Dhd-mYX^9=@#bZlu657(C#m-6BFvyY|)j3_=4bijy&OUu%k@>$$CCg=~xVU zOEvOUKm?PB&k2JV_lk*QWVF@fV59_yWF|}8H6k^A=)=Uk8g)g2l6YO003NC&Nx)`4 zQ0VH&_3q)rxe~pm0!XFDc!kAaUX4YSOQ&J#Jya!KeQ6^U(%YC1qhw+F3twX>7*MZv!J==pnH5{7fY$tvY( zo`ReBtyC@G{W2RW$c;}2Pcjj&O{{5d7)lUZ3q7eqg!X|)Q*oFJNc*MO`#7xn`qL*8vCqA|~pos6q0hCz!~xn=-hP{Je^H*eqLh+%-*!j#PJG8=WOmzmm|YtnA6@1~1E zY8}ibG9E6^Dk4k&0h5&n`a(mOyOUBMn=Z8U9uNvAu&PXWWu6jdCyn6-2a&HQ9S%4tKd|(!@i;Q*)xs1~Fq680a4c zeLk#O?9bOD4V1lrZ?znRLy{A<2MeVqH}5kr5zkAb4iO^U`QJCS(C2AnjbISKU9j<; zI(m-(o&_*^c789iSSvmL05e}GH9Y#bWwCCXCD+s>#~13{N7ffPz*4KrwmfO1LS^N- zvOyUC*1BYswC35$IxkmQVwx(8(SM)q?5R45XEI5+6h#y6iG%mm*^3+;mV@y~YqPR4X@VF@Nts_#MP&g%ShzR; z;ioa;ckHR(=8|c{lRvEObcwOGie+ORy4o4Wf!YuwlzNd=L0?MIGV|4^D5FlARVj7m zGS(%rv9bS&_dC zlQMMBX-w67g6i=(w#_li(YiQ!;km~|BmDMj2L|mi+|g9&M|>~BP?rk}3fL{jq`V^D zHHt|_(}F5L?ap}E#1%3uO*I}F7#b=gj@2GS#u_(gz2V@nF`{E+RJj8ciK14XsxSzO ziW&?}x*95iWIJy2l_!BbiQJH}T{C^+moMTX5}5y$-z|s1;BKpZx&7JAA%y%oKQc(g z9`}bnYZSQXOkme*4$jx8Cz5`R|Mcrqux-NTlm>LJU9NhRE*>x^0#93!I&7`xkN`x> z4Lf2%&ygX1%>=@IeeQ19LrjY9MBef4>FtwUz2G@}YA{@8&XS(rcUI0qGA6D~y|PnMs$cnkz9ehgtrs zo;zreNNhh*mG&6A?J+nqZ!jMX4GmD-jahM`j+Y*nm=rRmedW91Km7-6>~>~uva?qn z60lX+n28|MI1^h{GF2|eR6w1_KfG(D`RK-2OH`Bj{l^M2?sk zT05l`9DRKS75KD|+YRmib$;rZ0Ge45P653#Wh$x#a=qQ%VUv7>@q1leWB8BvOOgUH zpX#QSC5vUvgua68*@hZc1RHOJmQ68L=NvuFUFh=-Qeq#X>8|#sQ;5)n5_7ll(PqUtbsu{sE6Girre`%b>Fb=ZTW0GG&#RAJJ zjEUTpe`DFJ8YbsH0Ww`O13OW~i=~_~^qb#YHpk*&rq?gt&n9Usc%Q?`0D?BB;Kx)w zz|nPg^4|ygE9fgm&}YWNO!rvoKSi=|f9Tv2CkdyG6)Z4bW zQ4YtvD3bkB6uMP6c>hYx(MJYF77|^5@k()5b9TvBC`M|P?%Ziw4Qa@lFqp6Ot0@h9 z79h8$u2Q1JrjgOb^5W^7nDp2Q?mEp116d)tjj+dPgc3L^lT_U1M=l3xf}>AKT`Tj{ zMoTQduJI((iwxPs{jd@qE*~x$?Yg`hiTuuYTF+7i^0ejPFb29^ z!47zTz@(p*U9jZkAcfgTc8#$;{;U*~@j7&Lunf>8^MXR4H$hnMb3wX~59*|uf$d^# zs!(jyX3=ywZ?fD3Q8O_fF4?Ahp;GTHwsUL{9Bj3PM#T&$l)-lkd@iV;$q` z*Bvk4I&QnfFl=k4yP)am%@gsuDs=dwz38I{kviqRDTE6QUmkzEx>z$e0vtub@;C=$ zBw>%^YvC`o%fx(r!?lr^(rk-pM|!}(&KhT z)n3Lv1O-%)0r%(1a4|n&!8Pf7*sg;b4^w~jl+}D>rAhj9{6k$`9WaU(7tT^f(DEbf zD4Cg*&0g%=hGs=nOj-|(cdNMOl`F&niC`4HGK3@OGsOI4N2mNr-Z&tbj*k%ORhcV> z;273H*U_$;BL}K)H7czb@#z@v$H!$tK-+wc{dz^@Wm8cIt7^6FvJBAM0k5GD)#^~X zSeNgqiP)olKmy-=VN9nks%2FEN-F`h8=^mXa&d-6sr4f?Fvg*|Yk9-ySy5Yeb%x=O z@=fso5^1G6fZV2WO&6#!gN|4V1VSp5RZPQ>(=4bWy?9{;_>MG=0f&3M+Rx6$M!#^l z9tL%LW82knG7xUc?PjWt6RA#3QyW1Blpqq*3@q|Litf0=->jBRzu~-e`S{q^u~FAr z!gLMX&%w+wmp$c!&SQ<9J?t-qm&-k}Koj&d2s39sjjrbhiThbokI{`AGgs(fdVheh@bKaHg70PZn#m=dPk_9E!Ue2sI7i$qhsOa^~Gi0w6VnwgH-Diz{ zQo(2=?PF+WoVKDCl$VFiBT;Ck(b^ol=;|>^r+d;TJthg+p(_I!G9sPIBR6og7ugt5 zoYZ1lw+FN7#9vL+N9g_MW~VVpZjWN-&UuzSO-cJVBQNt;K@k?Q*l*goSQk1VyCi#( z;Q_6=LpXcOsD0Ruyp(K*Cc~i)zPXXc&TLyky#j6COl6$eJLB>lpS#A)hVppzcR<;o zK%ipJECIX61Yle$&Bs_g=7dEaQ^BDM=c|utLmb!j^*upeJe6iD@Igy}k;i3xvA2Y{ zxKgr3Hke9eW~#>~Nb+AsJdssZZRmEn6^>Fb8nV^~$-g>f^c`ZWAaTFVtf)xc;Bj#_ zN~zVzYIW&7GxHu_uOormab#yU4?VBvwfSNEU^leB%3%=&?M5`cMwhYNcnrO4jI^yc zhgVWuY%FECL}W7y6Scy=^=qQA;*MO7HyuUd`8_)ye+X^35fF^~{1V30!<2AaXDi&? zlz*#D4++(3I*KMr*KW2&XN38GQW?H@c6)Fz>dO3P0?ygM$S>h=ndGGp`TrLV zd0wjGsnMyK8kyn_qc(|qC@zdLzewPPcuW?vX4F+J)-87+0{OKiBmY2+S4hx1HbPon zbx_)R$Q0j~UZeC^rUW($irmZ#$`vxBpE`UMiet1I15X2h4Q0{Qz^B&+T$9r*9ODn! zVg?Zki)|o6Jr+nw$0{A(TwuR=p9PXj4Zr8yb@YaK)c%B*wXWbWS~YxMab{*{QfDYg%^@WpzzXc zlY;65Kt$*B_iQPPj&-(868N`7fQsy|jSAKyyD34v40L(xuyb!XaedYPj)*LR?0}ai{%64vjTpC1z;F=ql%x<>}1)JRX*>$BKDEQq~PQ8;LPAt1HRM#Tai5wt_iKjELN4HHtsH1%F`Y8|EQW zAolM`qUu8-V)o=lTbgE3>`Yi8(5vmPpQ?T`;|ELb1I^MU>Ex$vg#@Y~?86Uc{vodP z8yt3=bdbHdsB=b40@TyhlfwHqRkwJaP>|bZ?P&BUuK*<^zsmt7C+BE$v%j|(xHIa= zu9)ylWJv=Ls1-dVf`2qJ?jo}esx?<+ZAq;o*0QQ_WJ$V6U@$gi)Mp) z&MQ0LdwSRb#am%D{pJZhN_yQRl(hX5b}K<1dKY0~;a612%xP%&_#S7Ss@6w!@0M6<)-UakWDEN4b*fi@5S#w|15xs( zJ)?qG5tV`dO9Mv+IwrI8>q5kYd^WLgyh0F-!7&7*@U3*ZiF(iJdAA{ktlq}ck&$;2 zH#%_xgjqVAK&WZDKYO@H%RtBz?4yLzWl7J^(D3UXKD7Q|mjlVB&%DN#m0Z9{lGkS8 zbyU-*`^OC0OHMsf_6_GIpL5#3y4Oid9X5FZjMT-+sm}d4y66bXWnC&W$rY&Ig8TX` z_9^}*Wo7QUn%!dJ7;No1m)(l9Km(ZKXy2=MyI8~Ko^BaoVPgvoRRtb}(jQ92@_-O# zxSOh=sPLGOX9ZlyEOm+67NHFala2lQkBYr+-^^z}hp3@Q{f5VAWIo2SOHb0V6YqC0ZufH)HXB%LNfi~D?>G0oZGEeR-X2LM8KBFuFh}!3csT9P5sb3wm z`ol{qs?|fY(7Ch-JZ_f*aKHgO+!GOCr@4DO>DLHu+2l8*TgW(yvhVPL3_uq&zI#erpeL|#y#ubo-kMPpnFt) z=pzs;IJIaUU`-zCjYIVIR_g~QGmx{Fz;=@H>^KUu;H}4l9pOu*`sN2 z;ki6q4eiB5St{l&H*bF%ucvP;_ltbpkW36o;%iLdvZQ`{~9eSo>9P>TFp` zWng5`Jv+0S^%dn}|8Cz~H?LQo^zyJf$Bt;gAJe*e}%C#dY^KI+Vbk= z?Z<=@vewUe!&{Vg{nX=43=Ny667)bsHqyPt@p7X2<){0-OzEN_BXBd^&fb2^zCl+} zYbc1z!+`-N-QjEs0DGW26AV5#r|att+HuP1Ey_X4jzTsumerO#iMP@WsUi2}d{m0p zJv@9jdouST*D9qOfl;YeW?1@Y^u$`vusdG4RR66cjpT3W)B@Vu-+93y_lHQ~t(e(56(ZWdR~TdZJ+{=;-;tW7nBc z*z3D{y1xX0yJfnaqJZ8`(`I>L0!Brw(vMd9Uk-CYpXp^Y)H@CG%yS|LAfu6aoMnc< z1NXGioy&1GO`N}bWJK7xI8ZLfNGE(yAB3prFKOuRI{}8K z9%?$6XMaA@3tlXK&)qb=<*u=a-yc+ZQuU`{=7u_3??m}NXHLlDQ+94-NTR&%F=^3?K2PeEfq>OB!_JW(Rg z1xt=Lhp`lo+xUxJNzCINKY~dHNWw4CdE*oDW+w=Z;1}kwVazo=yacz3&&Eq>zNp|s zA+Pz!>!U#)@M$7t%kdybzCD_5bEr@Z_+aruqFeN>>IP^^ttOW`Xr&*m@PD}Sf&)`4 zXapKMM?-)(OD)V42I?4+xGQ4H85%GBk}7^EwWbJM>7k|0q$i8ZYRnCW#;l3@J;KAs z#ALQzI+#)Y4wQ@s*!PplpgL95{L2xE59cSspTgqbDqhxjUJCHt&LM~lkLuOKJ>ZXJOt zvmL9_gjgB2@bWXlYD(L^4MiGA?OYhN5FZODUEs#VtwUYARcn!fn+qOMz3HMraVw68 zb+QGL5};=WSfE(Vrs_mP;-&ahUNAi+#0D?M7k4E32Sm#@_s|s(QR$ruIqbnt&dxTb zE3IbvgKX;0&e>LevD2ip1_O3YgpcyA3Wq?}xqIxat^&&odpd%`3*itJFR@;w&U|Rp`?jHIhR#wJ^T{~dBBH?}631pU!9z6^SkSu@A zs_W^K!1t6Q3m<>QSL7F2b_O2a=Mj5tU(ufB@@N~#9zD-eXRSD<-fcc;b60Ce$0L-7 zVZf0TS5u=l3+aUaqIi`p`KtW2Z<jGbcSq60xlBg&X9^HS_K%wxX#n)V_^>Ck+YuRT*Xhaq5OjeiX zU1g_iDAiUFRv4YzKaXYU260zIk}Bsn2Jaupm;8vn zKO{q`YqmQ#SX=Zn#z^1r6?tYfo$^6L%|k4@xrugfn17KgQc;SUNz~jt$5VG*$vynr z&lykM`LC26!X??37HcvDAjFZB4R+%g6yynUEuYltS&R8L(Ahg1Yr-_O#=0D4GqlP( zzV}xe6_3fL(g$jF5)7A_CDgDe+B#kn)qgz$AL}qM2@TD4yTA)s6?GfsUL5VGOUSpa zolWFTW>JUKRcEuu;u(0A=nQq=%%Zo;hCxEH9k;#ZGbTk1N6bXM!#Ob_{-x`J#5>{V zwP&mC=r|T!Y!<>sn53T|pfv)q>1urO3oNq1!Q(l6M!G$*#ty1d@4T~(dev#J)E+m< zu&mXWS(@_Yo5y5)@+9Y{OCH<9H!sbYi}ivu*lbMXexB}*Vq-<8QI7cBa_4CS{)qd47FxPquT<8A!bK>~} zxZC`s&iG^YG975~bO0{f@96GrPTRRu$QK(;59l{e`%@2CV5SKo%9oMgS>C&$EdWO^ zTk&iRX9moLQuMYKDhA?~q=juE@8IFBZF4xq4TXeCbGV;gg0qo2o874Wh6~IAGoNGJ z(M+)$;G*MN#%2Z^L64E?WOM=q!9lVEQkDRWv7XB?=tr_B{raJ@$zh;tp2KPtw>JSf zJ_5n4=Ob{uux##yV;qfRNZr<^EO%Sy*6$zThKCM7;J!EbIZ;0Q1)>1~;U4lDw!>W5 zciglnD#t|3=|)bO@aPiWe$VpBP*M{WbrWQ&bs@`sYJL#-3~!ZyU}dm)R%#u^*UdfL zEdjeNdWR1&vMsXGi>jrFvrVn5HAsRqiTCz>=iCh^hTuym8~i7j#BYJ{zG73rC1^W*hvZ=DYcl1*)se z{TT8P<)P6spwB`R;TGMy1f+%_E|V~n+d^qMtpFJ=3~yg|3=e12Wmf#TKTPC2;U>jA ziLRY=(Ct|~EgA`95cDL^%rY4d;t^=lzp#7(?fxo}xnUlnQO zz&2nu$lTTTBe&0io=M*iTh z4OieIn(}_;&(WNZ*O%Q=pjM@bzP%#(UEQj2XWQe+@uBoMtR=>t;r5P}cZkgeBs!yH z)&Zc3LzgsS@zrBJ&nAC@d3+aM|3%N}`25oLz~XAxv(Y%iBlGEowhpJpqp#zSHP7Jg zpOy}(&BlXr!{6wP7dYZ@ablHKwAAqCS@h!@X5HH#BI;1$+2Js=8-6NMz_xw3gZ4m; z4{oI|b!%C7Da(%xMT2Q?yrAO}!nJ15g5+;5&N>;ZZx_WbWoy@Q{PRY+qS&GF`EnmI zr~1IwWVb0@jKRs7So;T-{s!rn-vFn;a))8hnn(H z9HaU3qep+Ns%TyUIr%ggK~H}EzGG6}pmb@R-@oeytgW~pZPLPgEA?Zzev`2O>N}$p^@`&2wdhs2_(k{1D>6}_h@}9b7GAoj8j&?jlRwz@$MZL0 zV*btZ>uY;HE+l~2-Fv(R9-U~?3$2^K?Y=i|a5nsnO8;+xmjCb1`~Ur4%!3C@4IV5V z9V@9&^vkb(EPtNF(@&wHq4Z(h{fiF|g@FtATDbax;cZ{Pg5uXh=v*&&9WZXZ6@H_H z7VOO9&ZnJ{->&!jTfFf67i1laLD9#K^}y$m08#H>x8^*t_)y@>Nv)`s7cCFr|PoMofH! zPYkY5@?Y4ox_TbM>&f45f3^3R*+3;3w?ub(VMAT{;*)HRzQhVkcs_5UO0{Xd}O zZ@fxAT+AjoUc^DkILRzJ>y6H&HM#9uw{}{p5^-@T$YnKZTz^bog(g+%Eo@l)G&0w! zSmfB(E3oIUHGtk%`~nAh$g?O-V61=R<#j-@56{fb2IVd>r`3Yfeh6nwLCo+c;aXE# zQd6fu�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

F|>sD~gz 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�&{Ke(1 zt&NR4AVSc+B4rpH5ttuXiOQI`FoBHiYo;a%)}2+^f{Rd;s<`1IauZLXiO@l4o1w&H zEz(+AN!;^e%Z12l5pthIjs<)kk%MY-bJ0`AkD6aMbm7oQ+MRY~TONM}s*@jVZEy3} za_=+nx|F`SrDXX$-U6E*%8ABwX`A;hVI{-IkF8z}cEB?}Ge3Va)kH-(yulnC1`uL( z7UPPR$%0;YZI?c?O>(sDCf!gdo_D0m8tPv!y9O8Vv8QV+&O!~kSb9Mf7Huz}7f=X9 zic`QwHm=y*L9cpq4*^d!Z+AJ*_sy_{y`>(;qMUoGtbDEG=2% zfaR|$aWhnOavt2xtripPm~=n%YNO)izOMP$e79+K-jB_0Ck^bL7&(CCW22_HeO_em z?FN?x3gbVfKcSq@(8v-JerUo)=I>f+aG8%n@=CrZ2N!|agRafUs7Nl#TtJQra{`Q3 zkqjoSKZ&2!AOYJWI`e^)&*dQ85}WAWP+3z`N$p`)o`9fk1{mkFHPqGq&0J)#a`cYr z*(lu#p&gpM89C9N%xZR!-2@=IRB?S4L0z26OKY7pdOwuv>l?H8nNw@b&RH6J4$S z3ib3J8tJ&n@ygB4j*Gihl7Z@Pu>uBpNn@dZ6UOBOJ6!}l@__9`uKj6V*fioc0_}BG z2Tct>YvXsyosl~BjVo)wJ3ll>IqiA3QHYPvv3;BVy+{%-p8OD&Qmp*P{MBPu)81HG zOk@lpZ{R~|QBg!S3C(%<_vc^GqnDdLv+;_NK6soFN*W^>)3xI2{SpU4} zjHiosY{F!^;r|L>%Bc4z_$<|uU_wh>{dPk%h;S}O@pF!Vk*%$XkjHT1zIn7}|p27|AFpanJ0k37hKJ|9yYwdQ&x5!?G7TsK3VcQ@pky^oL2Sf-G|3%|0y{^@Zm=|2azaZnlo(it9jzG4|ydTh)dG7uyDoR=>5=MG7N*6dSP%}6~1$x*Opp1YV#_EAj1!OF!g9fIu%eCyQ z$5{q`{wJ^+6KuJo`#?WCH}ge383_S_OyK%2-yG1j0i#T_(ZLA7!F{{j5soejq|eqn zwwat|s|@tj{?hLUy6Ht06-vvpOQ<>?O}we9oyqn-Kl=Dz&dK z=mj#(lY@i7RSc6&%=!XXH#c?F)f4mVxv%>Z4o<+q*4#`=M^9g|S5N<{q5b$oMz`s} zV?ap-6w!&#CrkC#U2VendrPf8UgmXDqa4hiKgSdkN=fyD;o1lnD)~p!(PC7)$rB*X zw|U*EDBXMh;2s?;2t$*OAr*hYT4;AYGMFxA34*rPF{*c*UIK*g^YUO-N(xhW@BTs z4&YGX6nV!E2dkRlyRNd4ghBo*=+MoK)$XPDK(5ApwR7qF()>5x-cxKm5O9!tzO zx@P(07h77&e74?3#O}5d7X{;ojQWkJ+Wt*wEvuRuAl=D*%*=}PTK?hSwYIf-6Aogb z#iius2zz)=WvPA9ba}e9y|yo#v(;{}tN!>09UKiD7_6-35g~u363j-DeXdq`Bm=Tx zQ3!OE^@ohI@qcatFeqr>sPTqAAi_8F2P2xxYI9W;DIFtyL}bJiJBx@9jbgsNN?7Z! zA}iJyQD?w$0>t6mS8IP_9BdpA4!n%ob#`(>1wznT^#z<6aPqR6RfxrSEe$;rv}*7LDV&Um%#_IH;LNFe=%4uME4=*dGV zVEpokA|@s#84_Zz-|;=^hqXaR_@j@zy9pJQzU=VW7?`T|ge7G_&qrT~Mf8QwWXkQo_8r@66l0gb4isd)^JUS>$SKsuv=eScOKi#A$A zUBz5Da1pdysi>>NP63S^*fU{EdTcG{T6y^zbsA0qm>oPp<3oP|i}DUg@^9S{B>YFNMkL|_5*JFUgV9C2~KrRlRZI4vEWn#i+-Nh*A0hpo9P zcT&EY_Ry9uP~N3?WI_j)el@X(<=4v%uK<}Oh1Y}hBR;V+6GK_avN4!cBCfW!Joa>6 zn$6kypD#wrhCvAjH7^X}mqh(34GH-#$OWaFL9%U6*N&Qhpe;-_XaZLM92i3^tLvd~ zY6J+Pk*!u${G6Se>zbsb?0LhGm6exG{QLL8Rd#rMJnngs!5JXD96Js2-meYyiRtPF zp1oeK%gHomXC5D)EbA@oj~sbFJz51A7j#J2FEmDIxntb2(9?rUjD2r-^FCJ|#{D;} zmunLm$j9zx$8}Qh>*6EHg6l66mI2 zGnlpb@9d10ilMBqFxbtMNmg6ibEVO-*|S+zc9=&@#VMh^(pSop4_e@(fJ;npp{cm6 z=jD#0nPgwg5jV(|Egh(Ra?*&~zFKwo+PdvEk=u3e{xBs;lu_yV__%NdhNMOcqD<~j zmjY4#JR?mt7N%xq%0{E3swIjtGIF0Y_*_TkY+DuKpllg!B<<=zah>VSuiH>7tVuHx& z_GgU~-!22|d{X6)mu1DFp=a%)>)q z_2fA_mynf{1J^eouk$(>W{v0PRR%y67Z$R_apxx-Wo8clOIjcK@ZrN;l>wS~h}6-{ zZcRhB3&>8?r#*+^CZJp}Jkq!5(^|VfCdeB89UAU$T5IzQ+h%8Y z>(KyDWfxZ{ZZP7LGqJj|^6%oki~*bnL_8c^S{mxyxdZxddNfk~(c=C>-64ij>{Y== z#R%&=kSi;vFtE@QDha;lA*y|Pai}K1l=fc-zz4W}z~F_jG~W9v9@zp7_nOV`g)6ig zs*v%mgaALxU5(3Ap~xN~eC?_SE*#JY9bQ+o99b_$fn&h!q!Xi>{h%JqW*Ntw)RUj3rbAYP6*TZ-@m(GoY(H#Jn*og+j@Q$<9%uJIdTL1(Ugxr zO|WESBHFxe@#|TzadALf62IqlBaHjs>4ZNj(E=_w1}Ll_PgmM&EQbX zD4Ih#&+IxfJWcA7gWhjY48*kDP^H&`%Hi_X{^SnDz{HN2Ka3D0T;v83~`armXDLjlcD$L0V`Rprqk3X$SvW z1B+2EF4!1iGP1aw(M(Rj!6OTk4#gEG6NOt`US_Q%@b$X6O!Bze2XlqRV>E&N%ZrKe z_N9|E=}HHh2414e{dpGv^wJ^e4r0Q=%>^jP%M-Gy{HV}e-JPHFeSIcq*D*Km2U`2e zC&}4Wes>2`BqTW=*_0~X{^zjS9S152*59lbj*fsG6o%35dku24f^X?;A-ptSESBB& zT5qYCx$S|#)rtaX)sGSUFU4x|Y&P(xzgH}MZdpl~$%pfD!dUwZu333mc|$sc!dB~X zyV9n}~%z4o2C_(q4(t6`P~85>ekN1U zq^BE*3hwUCL4+Y${Y{0`ML>oT2#s)XeEu^-EO`6omhOM;syf3rZG7g&-I__^+7H>O^x6?s`Z-J z6+vTR9(qIv1JinvZ4egU4X?gFnYZ5zd;qM5Sy&Nub36I1|zCx!tqhNF?V@hO@R(Uh8{<47BmxdIZ>8UP%)ew zHm{G_S(e0Rt*ox5HsoDwou2TY%U67?FfBB_Jz3t~`csovkB5V0mLg^Rjy>@3f)lS# zZw|Ky$*!O%iTe1*OM@JFCsL(?Wy$ z+4&~b2c262cyduw8yQyv~oq(5$g?JL9iLO~6nBctwMQ!&ySk zxT=JUf=W|hBS3f?GQH%AZmsmoWMJ|PG59@{Z2fW2Lt$WxE|dzD_u;pJD# zq;HXW5U0)VYw-}+P0;hyc|d@6jMXe(6zZwZeJ^hhpnRTrHT{)idg^Qo|M-K`gw%5 zV8c)>L^RCg(l8qv4`;H96g}nq_O||?RY75?L>0nF%@XSSgrp*z8zgjG%>MB8-_Cf` zqL#$Eke0DW^cIh89z8swdnf!X+pdH0U3Pb;f!*ZqjBL`bVRq2GEAP$%MP!sHS)i)R zqOpeIB-UG8&)Pj_KLnptl-rhnWL&J8FhGbv80D8W`rS2#&VxRm#qTc4AtCXlcdmZC zVwpQBDY4o0tY1nH{|E&OhmdesPbATt=$VNe%xpE1 zW4)X{vRKL-i@gaP5|#^~aqH0XsM|}g^*+N@GS?V*XK%hKLfBqjUu>Pwr-1Nk7i;`-DvkJsvt8VJYk-3uQs z%fSOHeC0X0v0+&Hp?|gJ#1^USDyZ9AWCN`mg)&ix+gd51_KO}X`1))moiRK^XN~9? z=d`a0yI{Q&QoM}~){?vijhT|wZ8AfDKgj3TF6lw1;#Io>h-V;_!rN%||Y z`2DMu3Ee<{KOq6ZWKfZ>f^hNjJ=iR(sRm7t*<3MI$v8T505er}uzVQ`F%SORM!uJa z2OCWP=T!fA7GpZlul~AseVy>y2kLL|r$#AD$bW)1AT3R8*Q54$?v-~|3w6%>(F8RM z?+~T(rqem`!vXwn*Nj?8<-Ei&^)X$06mRK0E*|bx0w*(hcGSOyh*>~Vb^Bdyd9X5h zT8-Yug2g{gjx4V*nwa=-CMKdQ<+n1=eQLb-&C8SW-HOrx=)3tVuN6c=&luyE#ji7Q zv>Xvx{qSv$bI;tPjI~%&GS~?1BJSTn!W3q4_a{}c3(jF_WY7wR7DP-+>bU)QQyARa zQLJcmdvtVUGRCr8Jzfc?;7D9NFT!D|CK0vK)cWnhd%sf9d*CD8$HX5gN6QV&dt5K= zsz%=d!Vew^iPN%^utvQi&0BI64e9b_t#$827xvtn;uKxJ@PHdbVX5QIzb%WT#+(aj zR9=_g-hBId=!a^tqQ-6|(bws^3?sP$u;SBgcG(1!{Buej!poN1YQHZ&r5LUoK1Gl> zFYOy4Qq#rp+W0*3TjA8v_jz#~4Zo|s@Z$d7J1y37ko^T()k=nj@ijW1fkYY_Wt zvcZ-u(8o!GeMdq&DrO*00|}G*)&F;_pgck9I!IB9K`4(tKHnTYsNOi1r9bAG*qWW( z^^eaF@bmFK_FkBHM+FB;kP7-fD~x8S_90@)&ja)P;Pc&jI~{`ZJ3W$BfMAUy^bIJntO7vEf94H)fed^G64Bc=Ui7w~BRNGPi+n=UK{YC0&G zY2$)PTHv&lbz`oYU5}$SwG2lun9SkRuy6?jgR}DTKmrG2iec1OmoV@&JOd~U%@NvI z4*AJk;S|G0)X%fV`TXlL12M*WmrlU1ebMOwK7k+p!uaDkAcq!4oWBvN^tD)6p4&nO|2oZQB6Gv8icYdI2$cVrnYT!x8*Kc_f~ z1EmI~5<1Ik;e*`{6j=f7DG85!RmUeM>o71vzR&Q#xg#^gJ&w)>pz**Wj~u7Yb(u@T z==N;+>G7saxj>5jY#!!pkYSKY|A$x@+P`lSM_jfT7%ZIDFqgoXn!^|4i?tZ^gto`5 zIpNSqHMF}?jV$Ue@pmA)!mI7@fYrpe`5xDtUd+j8bF;dCZzV|$k69})vZIFpyj;B0^!TTe6~u8J&y*dFq$K5 z52`2+8kw>ffj%3o32RQsl*VXoYO@0PAM7QcSyl1a*XQR`p0+fbJqOoKJmph)FlyUv zmvPZP$AOiJVJ%BX2-Z{Y)5ZS8IHztEil3b2(O{L&!5kAa(#x|}DlUBOS!e*_U58E*N?z%E(2Bq@QQaURrOeK7R^=<#5(13uJu#=BMYeiYBOt|Pd% zwzj=b=wbN>?zq6JP zL^zFb`@;v0cn8@#Y$g%(mC~Aw0aZdogu-ca_a8!4{Ll>zHMP0Lq??Xtz#Pfj24pI- z61M>vnUZ7Aa^0r(T9dPm?icTg_^#s&E&=`aEFa)Xna&w{K|@ zbX27{ZnEc_WV%CGf`4p}>KDQ1cy=%DNhc%(;ry=a24p=bG>6WaZSH}I49qTz87AK^ zpy2{^8!;&N;?U2sVvxxpb4wPyNboyR_Pto&LuKv`Cyg{U@hU1F5&A~qvlsHUd=X;K zHBee!tl3w3D=?(R6N~&r!!gP!u7D-`_=f#9Xk0D~qadTER`cqvozibP+rp~7rV!u6WS~1R9H9K}#O0vhOvrmeHn;;LjzMadZ_x9kQ~5IMkFMDCy}VP5}A@ z^hCRyPHE}tW7^Gh=iqIj zQKf%2TSi+iDA-mqo_aHH@B`>16ENM~1~Ka(7bX`ehk|?!GGl0Dytu^AlW(@2;5$bB z{(Ws--8Zz<^mMn~!@9W@rka0CdfM8cHcnl*lvnW@h$di)tkkNhHyMaMJUnb&Z}f6{01`Ld7Ve$+iaI$8!El$()n7tc*OIqM}Gah+hK30#ALfo}D= zdgOd{JzSB|yw6a+-t2(`dwK!_0-LktmWKLbDQS&{O;*-&<&BDs{J!lM3Yf4X zpCXCsIf94E%S%Db?5VgqlrMd2Vv`Et;=*zYH#GF&!2{@D-UHr`*D_*afq{XLXJg05 z<)hc~j0)*>NQj7r-;E+~Zf>rwkWPgi!f>L);r(77)zl<+Bh||8VIM%_>}l5NY76(* zDQI29AK-zxJRZV2eP6Lb@+=)oY&Y2|S+&;IAt9lzrjcj*RqNlC#rOmjU$bY2Q$EPX zs8=@#E0ekGnnzOQb0Q7lzy5VGJ?;8U+$Ii9WwYnb-g-2aI2a@tc zqvB6_J$Ou+>jQIKANV}G0wpcRGv{V@gh5oWzi33GAU_}Z`lhoQPy|hGgQP%l%kAZ@ zjI8YOA{5S9z=%bAV3)Ah?K61)5=0D$$jj@>DANN;9=|YEP72s(qU0h^LXj7NDBp*p zE|<)ssinoNv&Uk)G+GZ-K-+0pMs}-fO;yz&;Jmq*lhu`vPO~{c;>ZfLv*HE}%&R?xAUG7$N|>y*8fo`~@~y1|X*4pR zJZ1|yCuld zdG&I)dsb3m-@-KsaXD!ndF+$-u#Od_xuw5;-5&p=KHxXO!m3Y+BjeWw=57L3V8yF_ z2mAKDC#zgTYb0#b&s`{{8i>ozv_&SV@u7abL~+B`kb`ku~TeFpc( zLABf2G^koz7UJPY6d^iYYeU*sQB!-NXP~B~HQV0-_g`qT02)5`Q%YLq`TQay5ZL)k zG~c4BS?vvqt9j^ruvD>ppxVGcOEd`1cJ)smD9Icfi+$wG-N{~Ud3k>Jx;d;{ORN7} zWAS&P)`J0_g&WPM(v!dB1hB z1B82xLEgucy#ov(r6L+X!5yU~^jcQ&V{u0G3H|-j*X+uCawqLuyAR(^kj%FY2$<1{)GQ!Ax zPmF8&Jt3i6TE-whUn-7tx%Q{|z9PxyYbLMP>+o(Apo9%*$R#s*Jzg*7ib9ES#D@Vx zq30V@btRra>xSUX?~}jYD}EcfEl)4NEih`4x;tqig@=dp^wgo(`t5sYXftpKtJ<_( zh3+%!8^_)pVAz6Y^@O43GTW^;12-?gr6+;vhl0<_PNNGKU0pfAmY{s1*P@#_ zUj@d8%8ClnKGQ-o5#q;3S9SNRpysCZ5ZL8eZFA>mW)5}bSnff`3y24My`A<%CY1?=d1tbkSjCan zgC$xgui3xUuHcJpk>Slv3=)2~Twe&4sVSME2>j91anQZ05uXwU=Z_x<;-jhx3ibFr zka}pY5lD*-?w@noU&#M{_`o2qpy1II)U(-0D!^hk{Dr~#2oCKOIFD@r2M@fO`8*Ce z`UQ3VSXAjt_4j}Ex-iZ3TpM;J&O;UR|P_cw*3~ z5PLv76Aq7zsJB}s%+l0XPfkp&b~|YXPnn#P+6U+^`;x-inM9y2c9|QG4GxclgoO1V zB_)4no!~ydyTii8JQX&4r>deWMr=MeF@YY0YyuzJkl!jMqeV|8NX5(hT`)JEWfjpV z&xn+i+0hp0D|wFkZI?+J_x%I?eFbmtsXGYQK2uOsjE)k{fkIsfhjQ>G$r1G80zx$j zquvq*Hm(@=)d#{_L*VSWISM+v=lI_)U_OQKV+eGR`(kC5#}U_aV(MTRoozYixk;f|2Y=lu)hdfMg^j ztTz;nCE{P|U$PosYoqC>v7WCK{Xy9t1ZZGsvY$S2S(|raX9(GT;2X=A!)tUM8u}Ro zhZcDGfk~g$@>~hugO1I??&)@quE{xU1nAY|w7|4wXY>^cCNVwTYOPXFUnpjxdZI#y zt01+Z0j(5$l5ghZ@rDRA!u+W_nXf4O+ZpgOJvBwn<&l+6!tX9A*>`YgHJ;J%PMpl{ z{&zu`mJp;;02~@wz@I!pM{oGjzgG>YWJ2YtC9zSs%qMfRo}e?!W)u9Od8|~FlpsT9 zK9)*A3PD+#jE=Y7;BK(YWjh%G&nX)a-Jxc+X|yRMv}W0}va;eH5CGuC_=n*r|A_Fb z%|HeQ=1c~7wz%ZAB+ceBd5w)R&+QuZMxU!tPwnnDunQu{$2OoIpC)0E69evNns3I0 zE4Z#jLC2MnX+#796TdrOr9OB9tSg@a>B5x&f0MPcI^eha=5teKX66MJjo5~O$0dCB z&$c#_(5^t3tEGmsiK%KcuzAqjo}mLaS41ReX5WD^5-BOhl#g1+t&Rogf|4Oj2JHeC zr@EnW8+8wPs5ADksenmJ+D^IEqSx#afX>Ov+1C$paFbhz>zAKU!mtP*Jq{D;t%)`4 zmufLEAUrKl!nmbA)r6qK%p3j;3d+tVh2<8=0$KY`vOU2>blcCtz}yHpxTGW`C_=H1 zm(+}cZAK^<(qTuy_z*i;US&c4US4z}2#zzu>hdwk0U3=%U~u!xdXvxpMcrEl)w#9Z znh6%%H8=zh?(PsY5ZoWLCcW3^aOj2MnX_iaP*%RAyG0U%BXN@x7sr>U_Jd9I zvqyMYT5<7BZgO68AEFBXBEOldq?^~X`ATOjvnj{mMKm7J}yF)#smMI-ydO^!=VA&ny&dn99)Cl#ZNc)R##80;|Qi z!9Q`6<`*|s8cDat?EO=WK`t815}MV9XQgvrh>{pR&ZeLbwL)hXRKQr_B@WaYFJft8bq2RNsBewi) z%mnAG_u?%A)~2Gdqj)qj5eE-<2uX;tK_2?A+WPv>;aXbSJ)(}P2?0%Rc}sM z#nR<9-zw1c&zPrZW~9B<(&GJa1LB=TL`ked#F?nuyksHIA5LEt1$*9eR0t{daI^N< zYAtE~i=H7hx3vTXhxAw&v2_o8(fkF3-}EM1GG}yrdt5ZU~U?*8E)|oR07_7(jmk0Q>22!~Y|VLdL6!p}uV` zdB=DD{f1?z#G^~3!y^oun$83)Il=`${wn~NP%;C?XSjjhX*N@II4~ilJUl(%pXH#z z2!Vd~-&!LhBRx9(dq*HJJTTTzD$q>or(@k00?c_#5v}GoO#S#O@mNH}8a2C;=#@=2 z8=$N@xSs)P_uJ0+p|Bx(<=$p4Whg;S)c1shua}DmJX;W72;M0CZ@3^fllx0>5hv5^ zTXBa>`mhQj=2@D~FISt&MY7J4>>FxXYF&EBIIzlbAcK3pkoP$}Q+#%p!J)bh;`s#t zo+!bM;^X0sBr)1TyY2%X@cAs9>I zLrl%6q#Io?iD@Q6o$gQc^n7qrUZGf;y0P2aG#0Gn@Jz@7IXNPYEFmiSImN&s>9ipHBut+k3J}0YSGRGfT zUYWmcQi)gUxIsz;CE%w^b>>anb~L>x48AEC%=d-W=s~Z@xviAKF@n2=2B`4Xf4v!a zo%!eQekwZN8PW81Y2%*qh#)zpWMt5Cpe3#PA)G;t~3Ss*cP`t`P9I`{x2GKn-)K% zq~OhZ3i>)EgHL(_LY}>!?P(TxDqwwb<5T9~S(GJa& z$y%mgp<4+eUt;j;mYbhu<#a4Pwa$$EI&uW>2lQ%vZeqecwYKo*>P(}EByc&E9+uZq zBWMr$Ac+#v`Ov?-9NhHo6RkZ=Y&-zIO7K9Tetkf#NBY$>RCV)rN*9kgBgFY+wnjX? z(T3)UvjzABKb$M3^Hu2FoXGr=5w75^JiBiw!`FkPVSEqbl9(?>SmXS;2tX^nZcDxO zMHKIl0|c#aA+Shzc>(qv{Lamur;3@uBD#qP)ITK#eM5g2^|hLyy+x_;(;6L>wV$^u zddEffhQ#gqh|bZ>P=t&yALn<+7wF1xAfhIR@*S_%Y!Ck(Vbq=^fjOWbm6IV_MiAo$ z6*;f@;8{JjPWm?qdqQcEBFJ(n+dTiu7MsGP-(Mdx)6m$N%^&Q~47O8kkk=F9!&ee< zS|V2M05z4#OkNY3N5X`P!4&xyBQEy%fT->?AQciPYCt z=O|UO${J@l_j96S248t!9dOo>V&0E^A$=EsMl3X(^k=Pj*ErltM5fuk50{awb5T&# z?^L8v9zNraH*_Dk=lLu35olm*j0#gqQVBc>3SWKB-$>VpAzfQ2>C%JurcLp>OmP@O zW7vQ|z6Jlou@O$Uaz#+o)L} z&TO*|yOlfwRZ?g@%Vwos$n5~xU;TsT)yW!Gk)eC)f^5)Nr~3USBk9k=xXiktfr3|z zEUx-@onk|nL{`SexWrfiX}-6yIQz3D>;}I9sD|ukp#3Kd#=Uq@;=@WPC?Iebo_p3~ zow}s;A+-L78XeV<+5_sTvN?Awsv3b?M`p5;qL`NJLAte8VK$;rO( z^LrFhf}->D2fF=3lr6CUIF#VIhWgsiw1tI@a9mgE(_)Us#=GNd;HnE61Sm4L!ljHz zUMkae-eN#F3JPoE*^amtY41%c^P!!hM-5&aX&46RjD4-_*?phlE zd}~1Ns6pk*?3D;*m+Kjzj&EUUxr6RFTod9N}Y@W8auj+)~FbqLWqI z@X#5=kX>(7=x{4AGO|m=rE$)1PfAaVH1<@$RnL&6DH8LOkD%K5u1(~AVNl%&o@2=X z`5v$dJ6x|D1ZOh8(}{rfqCJcn0C4rK&%e=^nss_=z50zpzkPQ8W9IE3&ByHCY-cBwvSp`9DDwxa4evk?b;peL?w6lQvB{TxQ|LZODkJ-zbE8V3m4@P6?)r<8G`T5&#ZETKjPS>k+TM(@XcTRSq2t#d; z*1BGRTMlVw;uL6)ZP&*b0iayTzgQ3aszbxsJ{HI`+ew-1ive7|Mpj!%Oc6 z+3X>q)hzPboNCYfS;xo6Gt=xMDDWf<^z;;t)9D^J?6sW83S|D@PO}IX52k-0fvMDe zHQmc$t(lL}Hd+fb?}Z_CQi=}{341%fJVS#lx;Tm9%KR7)Pk@K%%YW|zF6K&RXt?Z_ z{eAV^++d*?4(Iu5yTo$buMTil8c+8Ysz`UZsE?;i1}Q1KE^CgVSvj4SktIAq;w3D> ze#09&ZrPyO(66!*wW%j4PN=NBcyxrpSKeYHt8?X*GRc3Ed_)qYTs&2S$UT7}j@mV= zM!>D5R6CI^=qDG{jg>(cYK$Hd?2bhpS5#OU4h@RK)zz&m7l)3|C$a&@3PA5_!2S%q z-Q2xME_s~hBZ<%5bO2qz$inP&d;2X^-VqCVGhd3!Q3xZi$2F0$C#cMXEAl6bix1Gb zKU$*fOm`1PBYkaYa%%%?&&!C65?7D8wM-uB;PH@KjQsiSbn_5-&Q;2GyVnD*uyp9b z-31y9c`2*;jeX4xbKEjby8Ns0tmLZNs4FItLmG}AYcE%@-`m(xQ{k9?MB$jN@ z9QA&phDv06Xs?Mshb!Tybx%*{K`mvu6Z;0{wF6HMs|KK7D zk8tJ{RB!=M5NUH81kzJAusGLDVsLx5gLBa!&A@SX;RbW`--)W9ryMV@a~5+)F`B;I z;$S0wCpL!wau52O4JBVyDWNjJqd6k3M3=aY>Cg+sLr=06!r@iC?XwpY3i1?-xWE<9 z83y^&&Zev^AxYte*!$}WVHi9C=WARX&0ql6l8}^~NMQ}=coUB|*%!*E+!YC4uzerl zbe@deoH<_Ha;j()(*$O{_llJr<%w}I$syt)x}sE*Y)4+=DIK6x3Lu9bdSNf4j({}c zpCcaY6f(kuhj;%B-OtQA2TH9sp)?)$M_JxiBKhs^&lFZ0H55%`NlFinW^*T3y`5Bl zezt3KDr*aIoiFF5Q6zd0ZCG17rM&YxJ}~`$--bm91!cd{2~8=D%Kdlz<19K8n%Fb^ zQsi4+C?U$PX=|;{#N4*Xr&VCNoGt7Toq=&01^b>wVj_z-LNq=r32wm1!UDeT^109B zGq16U35LG!oZkl-dHHhoJeK@kG5P+e(ipMc2%}@BWl|Y@9#ia!TaFaM6ADTSefRbZ zZablGKc}bJ6tdE@!ov|Aa4btWK)wd3ThxjfqaN8XsyJUN2w~rI{}jH3Ux9Zg;dd|4 zu+s_leY*w1_n=I|>XKn{4=t;I3xo_7q+|ek}(T6jI_G7+*pH=GK!^=Bw``=yA zUA>`yX=l|NM{M?7{-!b_r{>M3JfwD|=XV0ER8R9Rqm3H>7~{c(+k?I-)nh;kweu zX!p1d@p8HzCD;i@g+l|zZ?}t|H_L0 z@!SVe1|W@+l3uyquYug@`tl`41rz}=(9eP=+hsm*33wa&#Jw74QFiwSpb2S|!} zw&%qgYlBRei>Tdtdv3O*wO!@Kv1}iRm#wesZ~vS^i(7B@7UT_jt|~2Md(WXNuWJq1 zSJuBWjxz9=v+h8Tqo(HGA%W*mUq?ouoFIQ_?}ymo&;ArW-1 zcYK<8rKPnLhY|OanwsCGH32&rP%!6a=lwk?DJf8ig?d;0q#W$ZxH&kiR~m_3tLqEu zlS;?Nhr?qcxVbpNgzdPo-Z3yBN%iB$@z@Gl4Iz{|2P+rv*FP-7fU0J7xCg0B6BB`S z1p>tnfD%E?LdkD?U=rBg-cC%w-W|`>(^3YTHa~*0!qV1_xhoA@JCJR5)3w&E((ll2 zvg9}HCIcL^U{kOS27$k|R+}#oW{h7iz5=`!As(rQzWP*^!A66kqIJ!5e0*^Vtspcs zh;e~-pu)YYc42>FGJC+#mkbPQ`<6WP3b0y{&M%;$p#~!fkuW6$LCdsAzM#idfd=PR zNn>a+OC1yaa3leHC8G-!y5Tv%#YNy=pP06R#Gs(Zis;zy%?_J~&$~M?uyB4}u5@$& ziU~$}kQ4m~$`oC`-S|AH>+36uDxZPc*R!xQAnyQW7r@{lU7_IbUsz~Xre0B6xy8d@ zMJQ~P*O}#QWaQm+B3RsohJ|IaZf@Rfk%)s+KIEVL=XCe<&mWlc>8Uw7MrK$X063hR zwf7hHp8V^E22&6viH8LL$N$d zp+2W1CdxRF&N*mrr(|YA|MTFf(7wVOEmvd6Q2w-)uCos)(C#-USojciP0jwoFALmu z`X5u-=K5!``HF#N0=H3Vl$MyPrTeMYk?tw*zGi#Sp7!Kzs#s>5<8MI_>FIB6Q0x`! z1<|lHLG$RCrkf&k_YN!jRUnOdPt8%0-EwbzRKgsBGBP73)-&I|f&}D9fOwR)zLa!y z3<>{p1RD5C{D>mrWTWPD29c3Xvf}hP292IO<9vFa=pTt!;CBBrDmykinVFs7KNwxJz9zS&hxF_HBgkNv;6) zCmq?{vN&+?{0G|NAIZ8aGrrUFZtKJuO$7?S{9IueR{#V3v z9_s$Ly=uG6x`xqkjZWHig)VkF^@%Adu2xpE003G*uBb3KjpTZBFy{eof7qZQf#P3Y zrPBKm(qnnN44TaG{@`^F0!Vyt6O@MHC;FQOyba%a@L7wM17w1hoX!`ZpevCA#wv#= zQR`A909#pwaSF48NFmTk6~-@z?E;f*A7sm@qi zCk+Qi9S^9Hi2U%HmE+4)#!ZC>=&`&$FGz-AXXO+pxE~M|^yp!DB*M)5@q>z-ZmLj0 zCpg}o_Q`AvYQ2+~#hz@)^szr|^d2)*N>b9h1LqYV>U3CMpM4>)v8bsQEF=Kf6~D*z zDA=94YIui6N24OcSJ&1~Z!@VVfG@f4hKE6XeEd0ZWah|rGFgt7{M918E|a)Fqfa&H zlpyEoFIUK|s35+-s{>r>c27z>Xz8Df0`K47ny?Hw*@MIPx98cKj^q8GUvw%(WnULM zYxb9I*4)iww|~gt;f`jz?2M+$p2U|UH@iK)g|9q1vW~mQQNEkctZ8c6XnGNmJL!!F zSK1y*P~Ha2SX^IB;VK-#Mc|VZgQS@aDZZW^wsYI1! z5Ky3)ggp1q`+}dn?VL~S>Hc=E0@CkgXPd`FR)?)_7t^LoAUKZsysKo5llT2a4As2` z-n|hwDqL%J$aG2NcTSYwjY>~QxYa28*5)!XzD_dw6`Wdt{%s99r>F9z88pgM*qp3D z=0x)ptqvs0{+xXu1O{S8ItI~cMt1gmw}_oj@za#jv!*4zV&Wkvr0cztEOe}XT2wk< zR_bq+}s2zCE1XkzJ8czhBg+bGArw?dt@3V*=^G{+8RAr*?u&` z@p6zs;8atCg$U)yf4Ya`Pd&R7#_PYyC&=x0K8wHg0?OGv>- zZ?#;%58>Dy%Y^Ps=QZKGkZN3MvQ@ccpnZ3J>LPixf3X`ILmJ?5x%Zmf*+(i%l}8=Zh-4Sqke^6~;-_t0x&Z)xbrdtU!=;DNh`t5~V$ zb-c|fDOs8{65r>Jz{No&6oQ&^2>g@cVl|$^%fOW6J%}2*GTZ;ByVDDIUL9p)Ktcq< zJdmy5%Lv}PU1S@15t8{ET&=P@=?hg?D9D@9T3R}16;mPok5TE|=!+*CKCccxl1HO9 zf+%=RU7dL#TFTbR8h^Ghpy{o5KQ|T>+)n?H0}eqj5DM#GqkDf(U+EC?x~ITpVqzk} z#YL11xQ%AHNoEHsXh`2(Pt(zcaIiL0v9Q0er{xRT^T*cU&8{BOyn^B3VX(}vCIwdd?8L;m zwi$}b|K0^uYg%a^0Bh0LuV2B7;N)e#%gC>}xfwu7LHxsOS6~+S*+6VQ<@j)+GBG(> zMYtc_1DOn3C7zH|Q&Yd~j)DtFMEHHL%Qb-P@bXs3gvMlZISn6bbZ+pAxx2fIh`i!z zv|G_uiruIN`nBDZ?9pIaT3UR(@#BHVo5-jry=FsiC#P)%zieU8+gBBS#vn&uQeJMM zxdYa!;8iy}VSl>bA=u{Zb-DrulD$0(4mk-eIPVp4C+#(70lpo~e z^bYHHKz;!c5ivzFg%J>NLA}E^!%nNuOJLY{U@!eQ!;r8S{&qk{Mg}xfOf2Gx{4y0S zWvcfS1vxoG-Vr@|s=67(n1F45_$T2{(Z}q?l@*^V*>(MO+$7vyF){G-%}z`f$b3H! z4?oJVhD-rX9ysnOsHuN?{wOPQxf$1EMQ|`ROudf428Dn$zjCFxx6r!-$%fr1~c;R-@jp@9P0t# zy?}-cuiNDPv?Yn8Ug3<5mu%QY&%`9ZTnDOA&EF>fpj~UhgM)*@Ugw~&9n>OfjOKjqZ1q02f)st^MCQq0H&@)<*(oV4 zF`i`BuT_-^%c}YHOXiK99*FL}`%wUy+8!7I5M`DXp093hsi2?MqcUMO0ldf$2=`<@ zgv!auacY~ZvawN!<(EjAn&LdGsC+r!SFr~|lAcX#us5oW`Fb>6QcCQw~diVEcCSAxpAL3srQ zx*Dk|_!G$_rhiSrt!-j#q@tq426V=PJ8*8Fv`E|bU=(ab&6^Bvm-VcUwv8^2g%0T$ z00X}{T$ljxDa=}sBd|^d=*-7m~;3Wt(WM@sY>z+R&z~gl45*W zO$Nc8;eL7`!sKUkkY`SFzRzTiD45e8+`|)8@zv};PMn8-C9t$KFd&`88?|(HZcHoH zR(HNGnJG%$fVx>*WqI*x@A&>5$S5l0Ml7{*vU0$x1B+@r>^lUo1aA&N8AJ$-2+Oa9 z!G)nAO))VFPR5|zceFyZwMmRRy-r$P>ssZn7ksOOWAG7}8`>Ys4&PCnM|B}@1$Z<7 z+ah?gQ1=g@kg|$t2X|g;basNE-EYP)UQi&tRMx~ePKb^D?*04oqkR?mtH*XvkUm-w z@>+}s$+XF-nZwD>K6i8TE9$pL-pjcjkXO3Sf2aQr4KX7igq4*1GNm#V>lKqDPC`KP zslZPF^aN?(0Qr$yi3&&>?U^(G0cW4A*u64=#t#h--_$Vt$N_flKfx58n27yPCY2WN zV>3USi0eEJX{~y&|1+3o2jm2f$HDG$H zDhj=?wV%oZhB`nHqR6OrUgJwXUZHuqS^$hY^U;ibcgU*|>CGI_hEk$ZgcuPDL{VU| z*3r^t=jh?LClEFr2vn#jF(U4!a`WrX#b5T=Xw?}kQZuzGSpno7v$On`!o=%gUNHhd&{4TvI{{O0Oy&d2JN+G7cW2Fe&y`nvB8 zcB(DTDD?CM!^3D75YIRYA>iTq`t=(icY=d0x{lJmv2OsjNJ^^^87k=CUV47sS+h}q zgJbrp`!!H?EaY#CCVys&iMd~&{K&89o8cD&qiHWiFC(%}G^wzVfB;eX!Tw(iLgJAG zRxZ|_4N&Xx3Fd|55kXcAf2$7^n6$Zs5ck1f-`uEZEZpiyH#;3tBUAt3dyMn)xB+JX zEgb`8v97Kvco1oRx>7_>f{PU`suqD302q4C0fVc)qOBYy<)}`x-qBPRz!^0ElmRU_ zE7j9V1m$=?k*E~{c#&uH21?lM%uz!yruTQE(*Gv@jVwEd_Wcq~!V49HCLA3T1C0g} zhTBSD$o`U`GQZLsnnX0E7G0;t=to7I(4w%cOn(&c6y>z~8@5<2$vY;V_v}^S6ac50_Iv-%WOkz*$ulta>D9 zxT1&KGZWamJ7g;2#jY0+1K?(9#JA zAR8fpThUyZx-ss+;J~k6!xWvb1YaVgyH_XY=luyO-dod9P$MGIQBzRMdmvNru&ES+ z%|7}u!WjbCbp|t{WD9xm8TW?{#*jAHtT54Ve{2z|^s`*D6Y-HR02>8Syh-(xbs(_5 zl1@oU0p#sknz7O0K(5zDI5@aMu8?Yjjc3SJx03$4J`=z5WBoWATP;{7R?;$ugfBLE zOboH+D@4jGnSVgI~2_qg86WAue!2@tq;D2`m27hdvE-RUL&17xhQir^NCYl#c zzbq{2d$)A4+nx|8N6W}GJ~GC@%piF)bs(jQpL>5`#UYMjm#>h*bG984)9g%1Nl7kS zT-tgdr>bh?Eh^WS1U8iNH3M=wI?krI*CMflYcjfD}cv>ddkW^%_c%5 z9=zD|5g8HLzt*=4I+w$Fl}F0x3H$Badi+2(*@+>Gsdm^y9I?S3Qm|0&$mQ1MKH8)| zgL*dd{KZ^9!P6h^?-RTw2t$mZXdT22;6{8+K}qvvY5=PIbw_X}$cI_@EGu9=QR^qg zWks`m57`~zwtIARa*TxxkNpCa4Q7r{D3ae$tbJo*>{MrytrR(Fp)G}mKEE5;`~?RC z1CNC8jc2a?O<)D+mjhg%XUN^5J6&Xscxmfw1+s-cFs zM1r>g7D?F+FvN)$5LZ8jgUMV1NFmVC+pv4Z>p`z(4cz5{^LW8-5&lz-bZOJa z83rI@HPf}J{S>Z6kT(xX<_^4u=2#mlicC%dSAP7`T-! zs5KNO-5J>^pd873`V?OtDgS_ajPnP_#5mu^+B%0qdGmMvo1@pik4XgV4VQ*-ihXNq zhXHObE*#?yE>~{X9+?GRn4~~`VXB4AZXy!DKQ{H9=~We zGAw(#+Ja(^|DwP{F_Q>tPrkjtCEriqw`JnQVgfrNH2nZk?Ph$6-Uj<`y~z zCWVqP0uN{m$qmv!U(DtaPl}pTZUG?>gH__;i~TPQ8q7TL1&){AqMvqUWu@T$7Yq}m z&{TQb2*}#AO*b;Vvcx>qU0M;i@c2E*5cRg&(zhMItSJM4hwn$}F#T;}5^fUuG8mzQ zzpgg-xPe5>Y*lV+EvB1hy}hy##=l3rBLSNbE8r6G`*(lYg{e7WAy1wQ9BaE3=i`ip z3gEMI8aW*lW%)dj&Cd_)*anVg=;rZpi39I}KqaOMMTkZ!ATJ|hqn>@*=KA{)Jcj$U z*m(9t=x%^{^ka{mgu3OwcLDt|=sRU`VXR^cAUy{k$5QIu_cJQgwtEV{-2y{W=Nyzq zH{z$Kpb!Y$7A0Vx?_*)=gcjC$AYY=m+UfSMN=w;I&Gi8OdjS^mCM*5eemq#z^U<_w z?%I5}itXzI0nE7Q5a+(&sGJs=?=<;OJv$#l$ZcX%QwQAr$<&or_LTGY(Zp2U-Iuq5 zka#)KMTiz706&0rKpVdipU*laD2S?Z>*v*B>~9{_T5Qk=LYR?hnZ?L(xLmL_{Rb7*J3_Od&NpaiF<=1zS-%VdXtx&@Vak)6L14#|C>wkoVIA_xp^51o*FD{SR6P^Ai&}#e0ec0-UfyhOoey+*BU=0j=8k5&eJS0Ce`b1~1Q=6~+ZU@(PkKUm z%{N;GQ$G_`|uScU(8XU+b9`$v32 zis)PkE;Y&+*SPX20zpl)jvj3~v zb-ndCb$CES$Bcu}QIHGq*5#GDkMyKJwWU|BJIJU@VZy}4=~aJCdfMxmxBgw+%#rQO z@*Q^>CsFMt>&Yv`s@}A>7;2aGu%lrE76_7Y^IPv4kmA4Sh}zS5vjQ65@-? z`m{(Qi#f5XeH`xWy}-+~Wo?ABjqw5SFu%O^ei-GjDx53&qQj%qwjf`F-&op`6a63GAy)9J56Kd9*uSrPV2Qr&o4Bu#O8XqqParg$>mx>^~MvMNtuX=MM! z!!>B$rlX`%)>o!tqSjVXL8|>v4>=mpDK~R6o6oI>i}lg;;mrQxa?Q-4jE)?zlqh_raU=5%j4kwV#ob}*38JZ z-foSfKZc7xGM%T~BU^?rHxju%9Uh@4ok6E@S06SNRGz<++G4=!>;F4 zn3C5XnJi5F6*Wv=!m%VZ+Wn^HFb5P^1H;AQ1dk)AC?~PoGY=#S&Zwq&tM3Ec)m8H8vClzf$W?{}LoFk$f6zQ6_zOd7yzf8kd1V;>@# z{JWQ_98lgZy=Yrmwys-6$8}wij(7tZdl&+B-~TBW|9w;Xj#WACD8nBFfWqOC@NknU z^8zzNn1!s&jbH7SfHC50VT^*#(W=L?*H}Bon+aiEp>M_U{-aqcwphW=A4f*NEjyWx z`D4(MF-odT9}jwe4|YOA3_D&QC@+#M(-6g=9&5n0<3kykU1?PiHL_H<^-HT2^EUc* z$?BC>Jj6a{VLoqtt}1?x3!;vFw+a!%pXufN@LgTHcDYX z^jJQ_2bQI>fg3hsSDXU#_sL(Jq|N-u`gfE*!}RpYWA#nx zEyVX0-w#nFdcYzB{m(N|s_W%6J9bJarJ~m z4Xa{$fv$Q#9F>2^M-Z@-e6Z7Vx>@tMe{Lq9AF5?sa>QN7+WY0a6@2E*CHP+3$zqKt zT5Y#67<++PT&mn|feUj>1@7h~^jJTsqUZg2ZT#qzSycY1Dm`*=G-#FIHmM$3MU*MJ0!SIW8l5<1!^<#^Kd4gV!* zUaVU>ZE=Ovo9dyleGavrpAie$=-RDbjz56#ziFwHb=w}&w$M?h((6+9oHuqs& z?Hf-v1?E?zaCE)W@^X46h7g}O+>rCc3ec3Eo5 zR0mesZngoyfWgI`A`N@pe>cgft?q%{_xcnontiFhHdp;@^D6DbjatCiQ+aV_Kp`fPspH}xgN9L#h|7cwM-$$hTzv_*>F9cM;_60rr0t0G0 zfVu6L_vOWf$J21~Q*+PK-AvJ@l#lBlNbY65quI*b949q(@I5LJjE6^ti+xZn9s#4^ zvzgg*^CFNbfY4B*tf$BMe+Sc-`T8ZNr^{hL$jHgPcTw@y1ucH~L$y|M+pLD#0m#X8 zd_3?bUYjU7qf)~^u+p*q?tXq6&9TDv@sGE%vVt_#*2XUxLV%Prmd^F*#k-*JN0zY9 zIv{JUL7zab&vqD9bU#f)LjF$zy`GXPsB!p*ps%y~8$J#9_u}Fl#C>A3v!%V>H0VN( z0cmDvWW=t%9sEmk@oixgVeu*SKk~elmR1X(@h2v#0bF-bP*6K8pVPtPiJlUW4zEso zYVxJQ_Kwcqy?YrjuZ|W=*iRK*xLq_Bpcm0`08dxZ&$QLyiBGl`*3+v!ThPn>bX|~} zy9q2&-|MpYEY+FAR zx5Dk?Fr+U#rvO+_{TxMQz~U)7GStu+7YFy(&=8k)7ARy43CcwPZgT=+oPi^We8Y40 zm0~_>8BI-ESt0jJ2N&BEuyG64#>Cd9stVDVc7i& zER3CFii<<*>*YZT83bvJ&_UAfbggB(*?sN=u&qdF_;=mKpw2Vs1r$ik z#cta36=xYy2BVM&z-Ry~Jlu6y@#$M!U_&I~wZdlFz1&w(Qrp7Bq@c)iR6A6N_#gOq zQvP7GqUTd!C++lD7&o9p@JB^JsHv&xxdsC34QcaB4gkXdE?Y@gSYBQ)_`v!2t0sm= z^I*{SZk!J1*$_i}D$=?Cd^QC(^j{~Uc0xYSpe@;Gab5{5Nz~_yK5l>p-%yBsqr940 z!zshY;v&AODU}w%lJm|EsCn)4Ty3-4Xs+1?{jy8(`0SrPeOet`j*Z<61dt7{ho6Hq zFtD)mo?j#u08gpawTAYJMhVFq)Kk6{SR5Z1Fa=VnLK=r?KF6H(@`tHqZJHv^a74sH zGqV(Wu!B$GcM;{kaAwZ#icL;lur3XQl8zyXhK~VP)qvPoC1s=E=?iwB|NdEav53hw zH#hGK=`G(8+yQ+ley?8xB`W{<%b(p{>2oFipBtdL63DBcg+Pqd@$k9=(984kw*IAM zi21PVxasH|CUeNgbU41FR~^y}ErSpkBEl3dNaiu}Gl24)#>V;8RnQaeM*ENA1H6%U ziZ&LDm=)&2)+tGa>V4sK^mKVLXw3jkE}N^Bl1X;SsKa%Ex%&L{&@BX7uD{lPMYjOD zEt#Wk0_fyZKruh4-v{khpJh8i)gBGE9b~hI%qubj{4O*SlKHC)Lat9|TQ17A>Qaq~ zmtB0xgGQFXO8?*mUQ2&f`oDJphCZ~Mqi=M_W8~aDJe+E(4c``om8JzY-=ZO!o0|Ke zq7aKe2?A#27I+DPE4u7(c*5puc8uDb>S85PQNJZD>`U9(l&PbX*Q+OMU(DCD2>cXg z(}9iz#&u?$P6|Q6D8`vGm(%~!ndBM%OJ_1%G^y{q@{609Dyf?U2JgYyK3AO%_r&~s zldfmzXkx)yrRM-G)J1rdrTb}ey z7K)s4@Ah(|or}@(UYYQxjnARJ58VY4UN7cWK(n%FhwK7;jJh zz+&slIDl3lD0~#BvdT(ObZ@nMN%*s00|@RZP3`>OF~3hX0LR3rG?vQu7KI%z@WqIM zHvm4LIu&3Sw^TvlktH)V1N8jMW`}LZD@V|A=TKSj=be|H7sp5gg9gx4-M2&0~ zZ=^}*gQJkC}VU!!s&%;4B3)t5P2^!uPC>NBM%R4Zl@4O3U z;L5>EK{ZDGF)u+Z`@MpM+h3&^7?i5lEQIs(WMx`x;d^kmn7=uJlo(J@EKr7wf^Asc z9rzN!dPxK+-#4Whf2Fj&8zQHmFg1T~^#}dX4LtYmyC{oNlPK%RJVCZm{qHzA)sD=A zQPj9gf(+3NSHQLh1Z6y3U2i{ym}cpd)J)X?2q{w=`L%R3$$GuzHzg(Xx3CIle|~-c z*wcewsljCM*4TXT8Y92r8HUOTyO)H01&dm-pxKO(jUrwd#&dFRPVxuXOR@tq03Dqr zITP(kLf^(W^NHS<)oM$uku&V%;J||rc-Pk45^F~qX2bdMY<>nN>a5g6)AdSXDc&t= z{7H}N6X@I=lTAXJckhOVhV~Bj&z{j$ltEaHl>>a@dHJ1&9Av7F#|I7ZTNIJ5@*eUn z1ng#FwQL~HOLcv+WU|-`+GG1SCQkRJwsHSe(>4Md-he$fE89*PIrTy2iVH5I*4M*( zlsz`}5eSsm=x^1Flak`{g!njHK*t6`k_9R#!z8wM9_0LTi@56ILI^U?AZe~-_;XZi zR74sLa!!oIXk()Zdw}-`SeTUm9fpHsgOI_k_9m^ko)3-jVJSH(houu)N; z_uMyM(Ke*12)bWdq*tzWOGn_2hiFGeL=2#(3EWjZL&F)`cY!d@U)H$pK}O{ChmNy= zk*d-{xHul;et31Zkf31M4>FA`9G`_+*V-bkQ-!>w3=O+Mex6~NHGf1Y6&yFYk%J?6 ztThg9ExaJ<8M1&ZYo$p_)bI4-Enf=u|4Lh+Q4AFk87LHrit+xgZ`%CgK~aES$5+_C zkJ1|44D2&g4k)P4AlnZd8Y1&X){!LaxK?SEF)JPE+_qfGD&J!hQaC-Lfm&`i;}sU^ z|IPk#(SZmNYuK&h0(9)PI___|ch7BXptaBBxgjA(%IPk6hm1HAXf53~yJqBh#GELB z@}UM$0bv?Kx)Nb&S;CVRER}gR@YkT#u>_nxRr+_mmO-Y|+$=*Q6FRMsPW-vL z{e-Zr+?;h@J*LUq2_>v9L;DJKxz(mEv>FnQGg73GzTRXYEOCVHVzJRF=!)LJ=Xe_m z`gsx=G+cc`Pq6QoyEq&?U0+TBb&I8~keXT!mszaA$1K}B&a-&rNEYMfZyd!d4L1O?*U2I@ZO!K1VQ4fwNELm5u5+)NC ztW?z7fku*3!>8AvSq3%FMRyyEc~P&;#bk1$*_)ANk60xnpz_V` zXl4XFAAQTrz`$}L!vQok8z;NocKr^Bf}Px)2E@amytOu&aoUO82gWQD(+p z4A@lBuk<`SH{B5OT8c?QmT$+s3Nd^w>asV9!K-9oU{L)N2s7x`?vHy@?&iCvbJC%5 z(?uUMt>#MSK->%X?~?g=fIoo6`WF6n6MKv@E}2=cHx4`n*!U3VjZUxT&$>{R9WTCq zeZeRSGBUi?FTRSl8)|3>ggRlyO!3}l*bjj1Aeg2wOT%xVD!ZILY z<^AkUM^~aSJD1M+1uh&w07D8u`48#3&#R<Zi%Dl=OXn;~!>Co%R0amKI^1)iS$0#4ZS3tKzJ39QXPa(I*NuOlayvVP_#&U+ zh3|gnOD~g=>*;C3bIi3no>3I^++FOFefjMG$-*dvp3wR4%l+kw=K=ZIa63yCvy+I zK^H*v^?bVLEaL zVWS{oMGU1Yh)?Ey51K_>ANPKPOWTO79Oy#1-@5yD?OSir`v{1fEHaymr^^lBj$573 zyDj#fJZ?Y&AzusQCaCy1%MbQUT-Ds{GYlLIAnJ=u05lYWUc`T>_2AaozwE4p=@Cnx z7JtiJ+FBZyIXTLr07^$4Z)RuNqz!+5zim?f7}*F~gj&COt>}QRAN&${c2-JE@$Xw! z-PI75@RfexlhAG&F9BXg{9`*s10AFaKi{`$Zw4iZS%gW1JwF>+&Q&~!tvd>#c7E9% z2Pbz;jjvXnIe&%Lx1&Yn{s31UG!ty2f9AH7x2UstAy{~X7acoeqobw8#r=yCv4NmZj$I#)Z@|3Tef zwq@A`YWyJG-O>n1cb9Z`cXxMpx0Iv^k|N#RE!`nV3rKehv!1hOLm-Uul;lKdJs?N^!r+pl=K-hAlmYrhq(x}VIO-MJOu$a6JLbo}8XmVy%G+Z%(*_poLFyQ2o1|=LE+B&il+G`=k2@ ztQLSm{MG1c?#3R>iXf5nZmK#RHd&F6(oreH$dCxc&W8HcLPi?=6-C|Cep}K1gXKs260f zaDrl=5qH#R+d9H>*C#j=STdoY2?5l{QYsC{B+@QG)*3?nIq!WJlF6}F=6R8|SzPaxs}MpCwmSp^E_ zX6=SQl!6G;FTKY8L2{E$dL4#lE?0g;C!}xS99hRU8{@JYeL6A8!NK9f;xd)%3%Wap zzp5BGm}d0}TII&Z4ux|y)YMQ7Cz1Y73kY}%4Uc&_{oOz$9d&E+-e!x$7uMIqOU{j* z7e`~RU2zR)0695L{j^C5DAFHE1Mjw8!$Ih)_6I+nj2j;>qk!Aq#i@SFNCL_DiHDos zIKHJlf=>#MlK~4Iy-~}?J9lV*4ABnH>*2URO0?r6|KEEJ72B|HWS-P5swB<+bC3`%cEO{34RzS46Oc6 zNkORWc|E62Y}9&(NA}TYFa<>p&E>XWqabq;9}t+nXYps%F*k6lH}rV9D*RS&XDj68 z=0_UMRGv^GnG&cEp+z?GG{(NRK$_FdaNoO03=LZD9pTX4Abi0P@uq|m4u0(n4Tmex zC0vRwG!IL?+HA(s2q~BQZ4oY(EfD;=IG%|)BKqPqweE{B;d!?H&hBY1b{Av`6crVu z6NqE`AkJ4zSKV44t*3Iix!LmiJ?Ad3mA(pG54Szv9)sL|yY0F&q&apPBsAPcyC<^x zHxQ{8hg%yO9_*KTZ_~RlCPMa+M2O1523Ja*29C%E%ImAp2LqXyuORqGK|gXL8h;7* zj+Vbh2v5xPx!LiE@CY<+Y)x0p4r@(aWL!B1Yv<^kmpKs_a4A|-w#Uc4DJ25p@V6!H6{HgtmZsZ#qHS%OqEp?b{08(z@qQPL=cV$c{ zB0(S4ah3}awysrnz`{3o6K7QUR@s?AGS>N!-Vs5->KkZ91a%bJ@tje$Ru+s?WBl0v zug{?5^^eE~(#bGf-W#3BGpn!PKBa-Psaz{_l?FO5H9oVEinexiL`oLKBiQ z8_a4BH17y?=wmc7X&;z@jfu+i?Cc*cEj(B$;7}^jiZrkbx($n(n&!R+L2$s2M|g$5 zIA*+Vxd8cZVmv=29F!i?9kQj=_cjoNwB%MPN9>#qvj5gDj%{@T z3}w3BB@*!jOlNiZT!!hsUYtYG)kGriq`Kx3krG#6A)cKkDZOhoLpSg%*;-2uYsDl>6B85c1xA- z&Ycq#6?1cmL7gTtW$*9bI)_K3?(j98HDV9X-}ROZo8n&NnIp=iLlQ$b^`}*37r$$r zWMpK5jF`Jz#E=Jo2nMj?FJUiWncUu{46G@K(*Z03F%h=DrePvg`{#(yzWz_4Iuxec zI%m6tS#XKft>ok;TDl~hHW(f74VVFT&cgq#vgDDnWIrc}%98MVZRt<$gGVdi1kvgO z_1nm3<6;XW#k#$2?ZCJohCdQ=8JQS3?SiG?m6Red$5Qx8 zSv!tI#vicfZ<%Eo$;4Fkvhe^`li0_Rcu*dN{huV{)N*`_Lw7z30iZqtT}*s@kMC`q zZq@c~^nE&R`lddLxQ@;Ph%JMo_|@&@)YMN%ch~ZYczHSSGt3t7dFR5L3p(pz{bKv2 z!Ew(pVz#!rlK-w_ah3z3BBPhx4y_pn#Dt5`Y!0sCkT~mXE3|;*Y!W9aEooY*rKyRJ zheJzGf4bVy0oGT|%nIL&y+7aRd0tkT11)jZ=B#K{6X7mFq0OgfkgYtOD;cjLgUPTP zH4)fYQ|r3~JuBVjfOpSjr@f)!l9Gd0SJ&X~I6n0=6)m%G6ui{Fv}U#9P0z`I9|@@a z<6vWR%FswyVRXRbFjBP~o&+ya9> zj%!Vzv=wE`&c=p~kDdNGRYIIeuXSr|(f)0VX{!gpMhaGPDF04rKvPRqbDu(oT5<^F z-B|Ajiz2^psvbNu!Jsj#Xh}6!R<2J^pR3ErXj(=(=65r~avWcf@_DRGOni3MwDc$g zgFR7EQCa?Wn*|QU#O{^NQ9*>&01He(k#CY~0x^RRjy=I$vIQSlWup@bN~;U<($b&} zRkZZic__aZlW?0X7gsyYn7ed6g6uC4KoMuw`C=tOUAm^;d3q zhDS>wBd)9vS*L)=^7rrYqmO^6Bd94f0tMI-@klQ2Ti@SQ)!A9V>LwBJ#KOcJZtlq^ z6PNuKv<&?`L_JtST|)8beY!FEu?XzmQ~hk<#40Bo;fgS+xJkG%>hjA>&)l`SLPAOx zvt(TWa4u#P}RmwN6&&8k&Fy&r*0xD1=iA2tJc2C}) zM1gT=o_e9C=Ht^^ABWTtLD>n%$H%w-Bd|=DRZ>+I^CUf~1--orm@g;jZ0p94Py7PT zz(4E&3rB|#cZxbPK0i|>Z`A+Y_L>|mt}U&t#b>6PSYOmMw(|1vz1t-Ugrl|uHA_k+ zBHVv}|<*?HeX~#pHU4aj+KqAZ{rg#q1w>Y0vBIyhn3`MO?Vd{Q0KBcwCuKLbpdY2!%{_b z&>)J*TywV)ek6&$${JI=sGPpTXKK5V*j$09a`PKf}HW zkEXwNYQkk;gJz|N>`Gln20@6Lh2@QoH_PagcuW}I?ELcgps3&g-E9yB{k6Ilp0!k^ zzt+*h)Cd;=$IDDlk7TY~7Zwza>TwA;!E$l&fWn}~Xv_`vcyiypeA+E?1u1;C80BK-KI?zH{-y!ZV4fWw zp3pea4GljN3(n+$&v$PRm(a~z3tTRXxr9ohLEX9)TAlW8AexV89dOT#FrQe*xw^Va zqnr!$Md&-gb{!iX1+V83I^d_wP+t&eE^7E_>)N*}&|J@`Q~VqLWH-T6npIc#qcHWo z6cE9j!=b`CIdjZvtt_qB<0rTcLcxkVE0VIbp&?iZhq9JckSn8Q8Q2;o=U`*EngW08 zV8_@&=IAIg{!|hH9~KFcw}_-B?iJSQ_zw`)r8j7@quwWIC}jQ=n+?i+i00{^v$C?Y zQ!w%&{59vdW?hJdV>_@I!l(1afmhYPy#QL9JLE1`OjV5Ad4Q;bhL6lP+LFQS&YWd= z!(3`_B0}lB9D)G~^A>qimU->_)e#in_quR+9o{+Kz+Memj(QrEJ-m?+jT!s2MNUWe z(?)y)-hP{U+iw=OvN7Uv#3eH=ZG?094}iYyTUb&?KyuH{EzHe)bA))$o^?m?<3yb< zprW#{TuDjE_wg|rjWnVHx#H7mB--NQ?CD~M_Q)raRV0(bsOU((YTW^p;%Icz-t&jN z;9o8z8@1B~0IZdo@mXv@N?O|f_;6-1BNYi9H4P)vgbooqAutDJy|{X#cpD@{hlM*T zS;Ib(l9HO#fes9qO(Drta9!SrkbJQp!)Yc6s`s5Qo&3V{_wbOn+La$5Qq`hMrt^fX zGw!4FVdIBBN9bESJM(x?<$`BH=m)2bPDl=vI$c9e*oxljP*~hI?yV|ZLAf^`Zf;QB ztV&2o9I}bwXY^b-tA}<^HVpaES^ok&Z$Rg8*sK#*WP+D?hxsl$CFQ5QETBX{J;T7m z>+9&T@|b7hWR{ixFvW|1@iA?9BPI9@G-;rH!59{mmclt9ff-oc*?BlPxUAkp=J__) zfu~YME;dg#X94fO6F0};oOgN>4J78ff#@WaD$vuzKuac=1YJ81*g5d3mXeY%n+n0s z}Ssz;!|ycOLc z{QWsc#r>8bs&m{xV}yB953c|%qBi6J!?*iXmxEeA(O=)SkTpXN9s zk)RfNgL*@BhkSlj3F$yK+%caTHT6Vq9V$#ovzJ9LlTAssW9|UAt$F-JSx75G5Xc5T&Gnl)uw&Vrsq*yy< zu#vE}E5}O$=2R-ETDq_%*}V%kjy*gz3>D?j&`?-dSTi@QlK1BwAOwSZje7So6n~Hm z7mY^Jzt{K2JnM9`CA08=TS|iOmvvviq{B&%91P61*KJpEJK}H&`WZ zeng{@5mSqMO1e;HVsNlx9t|NBlMjnITH2VJ5@YBS zE4s+4GiUqTEwR)U1oyFsbGlG9`OT&)Vi4v97Y{G=yP(I4N@G+{zVP#vD5jomJUJ}p zyT>8laE?x459xnnH%%9$KJbD$h&Xse*pu5XS(Y8_9PA0*hMB&j7#*ln$F6k*h=AmS zQLv4P>9Sc~P9NMMS6;A;xvQh6Q?1{EX2D3w;QchQ3PuS{X+u)GdlYE8VYC!-QLQo`DXbl z59RfTWu#}LCu`U5v)^CXJqbJWK(1e`3u!(+C63i35&U+OC=M{=Hk!;wK7_RvjIyi1=*;=Bmoz^sLECd^6saJDlrs?t(-e2$$Xm-39o$IsyuxNu5V4!ai z{)e-afEBZ!<$467fmF%CX!>~OP!HFf$_^ksM@WVm_DMuZC-jAPOW9~DR{aF=zkNhL zc?Jf9sKUa+#6(m(uw0}uXy}w^Sc<&{kX#4|7uLZstvOKmHk1$T--`8)jtL8!1jDlo zLvIXKFcdyCK-nUT4*&tG#;6GiQ-Z;sh(Uh*d=UdP^udqbBIFUBvIhX@Cx{cA6Hw5; ze^ytNm;3k_KAPJurS1{{CXoka4wwz?+ENs{)q5q1UOSPsC|r@xM9OKezkS)3qN|_F@8$=5oGe#jY`B5O1MQ0!aLbN6!i26tv*CFrl!_M6jOD6 zUu-?m{4^n#PFp)=dpc){=C{c#G0r?a77&3edGQLWmfxseki@V&iw^O3SB?%3)fx$i zp^PEa|A1dZm41`ir=_V*5;$5c1_00+85F@EXy1oM)h+QZ^P@O1_}#)0;9r9s_<@9u{z5gXqPb|9g=iC0S`+=o*ve|*IYlYTTyTkK2 zV&g|;*-DEwIt&&MmJT%cL43z{T;9hLd?@K9v*j}(3~Uf3i;GDRX<^oF-}6?V(+=%5 zbie*Gv+JGtx0v?F6Mln<_3xcfL1*_rUFLuM8Iq^I@Q{Vk3JO9+p-~9Qgh<(!)i^_l z_I1D;A#&`v^1~Ehy6#oK^@~EJGC}8rc=x84+(gI#Y@31%0t!k*5qtqmgO!;>{aD@E zA14Hh!{g(+o=Ar_-{axb5;ThZkK2?H{Ea+w1IhJb>jfd5DC$@_coZm&8(C~cySt_z z%>f(W_7(-{8kwydG%`akVSd0?-Gu7%5C8~=?m}~8WpT!uhsN*zJyfIBtJAAWwK=s( zy+)BE42qSFF29C0ChqdcT7Cy;w3s$E1HG0Aj$iOMOv9)&+l-}FrJMTgFbz%sB8PDl zBTJbDMSnh+sCivg&u_Me5YawAKewf%WtSK>#9=jl>^Eo=l< z6grfni!jkrs56NXT-hTi&0}yz(8M^v{pu|HmTb@Rn8{!1@4I|of=AnHxzq zZUc4~>-%TUKYdaHm(QUUwS*t#0B|-iXSuy?YJ*p17=60t z?&dz7%l~`5cIftanazrt`tQ$3*O)Rqm=7NiG*GB?lCyL`jW|2qG`Sx%E`R1d?cWjs z@U%2Ou%t~aEyF`viqfzpSz^b7i|B-8sHng~-{>|Wk`Ff!h;Z@WqO39M04+K>vScw7 z`gykY()E(H&dA6ptG^wLqkTywfUB~!o3;_mP;rR_Cw1^>Zv%F0hXM_DwN-4U(e&r% zB~&BA9mAX)!%&Zv3ryBsAR#U-%ta)$`&^oR|Maz>uE5yscvHWstSmV@MYe`QU%p2e z&tE|yVfzm(_`as?JvLa6ma`(F<-(w0>Ocrh3O#GvLttyDBrA8U7)&Oh4aD+$9gPMi z5}pRsVMEW|tfqax6J*Hm3ciPAhbvGgu(o2enn8kV{Q8yT{43?w4=|V)>hVo<>!3fb z5UMs>MmF5&%QH6!K9YXGN|PCu1=m|dB=N(pGcI)-KU0bv7QWmlOsDQYx|pH}rN)7? zq;hz!Di8JwTHDx}X*@)vvjboQl%Af&7- zqR0E9U5Mf`ob-pxO@~%AmLWqQo8t_os!6?uij=^f)y(f9sus<<7KQH+T69_X+SV=W0#ApV) zMQOmo9K(kRCwO?9xJeWtlYD1+K!D{~+f^GtD`-AEVfIx0`N@vQWt|lgY?{->YGnT^St2a~GFZP2jYBPKv>b zmapE_FHJKFZVc-09kJ42(9<>QKKlScCoAij*e(%N#87A!oH>Q35?(t2R1`47XNDv$ zF53N`KEi~l0seA<&;8)5pf7PKuE&?K!uJ-)X)PY&s%nx-n!w}y(v!`>i;#!2HY*Vsk5S@eACz|&*YPvHC8bc2r|j1>HPs`yJf6)?G3)udOxN^Y~uj3}--?VeQD! zQ2d(?ychM5H7#BDjN74=(txL!(A zqmkZ?e_$$(hW>A?aZ16-3{f6R=PPnSoP~e`;{V+8r7BFid5x<=kF#YfbF-}Yj zh%ru%j`N2tw<&*vAv(e3`wE>*NMwnu5y2hR7)2_c5A#LqA+Wi*YxN5$coC5Y_f{Dg zBB0^-f8I@x0NPGGMrx=i#~thtBj`PV{WGFK>J#)3L^v-eKt=}`^#IhXBo>DCiv@&& zs|}();QU8SoH*P?*~Sq`OPA5UXIwXZblboTb47rzsVXtl(FwW1bT@|GWXeH>jNb1H zyq~B%8H&em!2ff8KD|eLA#BykUbFFIJ7m!HH4cz>>a4f)FfFW49tbvM#Kh33 z$c_V`^@A||Neei4vH&yUVW6yx1q_B*Hy0HZp_6))1jT^YB9Qy9{5Jub%Y~Qt&fL?J zOUY18EjKGmMPGmIP3Q9bJNTKJibf5mzb{ugb~QGN5m3? zP07F8s=B&;pF#OSr^m?=g!6CjDdhfHnTS>E)Kvn_Q$_}1@>jFs+8+jOvtTDLB?$=- zyK{UTJ2?1r{=36}@n{ijuYT^`eR*^T{s;(6pX4<*YG7iffC)8IeSly2wnbEL5+Ve) zKmiOBEIDfE>9t1<`~?1g66ibtnyxP>44^7gF*B#6Uj2SF*41rtA8EIpx5nAS2L)^y zVhn8jGz?`$*{>f1{icn&vdq4HZ(Uij%k6G&Z!D$)P_#xbQ&S4NwK*^d5h5uW37999 zLNlxtF*B6s!essOX>8q-Wo_jUJyOV1jc5A6fP(H744d z?5Rtr*o@0BYuek(bJ9}soYqFX0IRW6n2~lg_?~&Rvx~3!T-arLa;mDbJc0{M=Qg#F zjp23p-z$Oh&=*eIuznzSjE&Jx=Lz=pRnRk3I=0sj^?u~-KddvKo1Zsvpc)*SZAc;& z0u{CDp^(9rmgnsk5()E57Hux2(eWiFkhqrcYZG=DMu z8S?+MfGDP@A5S-b(+HV?lg^sPMgR;wU~QZ2nq4xDRe(=A@89!XSSHZ&zzKZnbDP&~d3X0K@NocNFYF&zQ+tdUy6A%W1AZlmtmpignBrm> z=j{J=f;!sT0Iw|U308!lJNVvdV#Rgg9I|w$AiR29yR!I|QRLmXePC1Dgp-K`RdU}9t=ZA@;QpqbOC(}%5E&&Y^$!FHot2;mUy0Rx_)Lml0G zDU-X79RMWe6~3aqMg+zuCBf^O$bBy@rDvdPc%EHI%;4d4_`9=X_CJM_C3H0Z%a_RH z>3n|AvxHL+tpy*yL?U4dE+$Q#n73L*jYX=~e_t~*0a0W41je3#fOD5bsLbO3yD_8e zO1%M!L5YcpYmJUyzNY`F_4pfsh|P+deRaM8udA;=KECV;J%K}j)W0_{g8uIr{m^_3 zUm7M>A)hULN6D>}Kq2GHnvlxeQq>pD&zfGR>wLklP^J_Gixnh8gM;4;TtpUNsDT%K zcfPp{GN7t#b&U(03T2qgd4cY2EVrr~6MkkOa_@)#3T{6k71 zfeFGrMinL=Hl-sVJN!BdHOGK;v;VI4)Ma-#QI%5q>FL48&v(asM2Q4I6L}YNf!Rju z5tZZ;Kn%Z!P*7D|9I?#Bp`di~@HT;#!-kwJUCO*8%?;HlmOjkv$}pJW+w ztd*P3R|<-%!js60qC^FF8)>70)Rt!;R@H@}kD!Urjtjnj=P?_Ui94guA^cWOz@i35 zjeq}61pnGW33+*jy8|#-Q{PzI9`vs~HddFVx+X$4ObL81rK}s^ip{?(UEJz5vJPq! zzyn`!X>nd&N$I1<#kbao6=2^kRqIb1;6$Mfk5sO@*Zu>KyN9=gL9p-6=a(bIf(D#ib=0=i`$v$ zN#=iRGBnAbQ~M)(GZ$>iAv5u&XgEx_%*|d!^83X^JUHMZy5*JS7w48trBv12)Yw}- z*t}E4tOFE~3qxLYqpDbb3C<4e@8K^CGd5%NIAuP$2tgGQtuRMvnP1?&Oq7w{fpb9^ z<@aK$iVFJ}S3W4R*#+A5i}C{Z$cRW*zSViIPW}X*Q@C(!{HAeB_<0ttrkh|Oq64+c z9676rJ<>hGg>?N5q(%V>9U`hHV*vCvM5dvkrRQ>1W(=oCq`uXnjx~%uy3%K=J5WQY zDNEqx?X;=eXz{b^m|w7=NML*|s7Osq-7*osDXgHP zq5_+;U-R>O?59biY%Tzd+OXWNoE9bU(@#WPFwE=s-~_k>A+ONcX6y5@%P!qll`esf zeK=Tx*GTmUZeMM&ZRzMHP$}dP!tx8NESZQlh9SfT<}Sne<98z_zT8CzANag@#cs)jdk<%{9y|6odNhR4~-AII$= z3m9<$ldvT{X1DZO)R2IgwaXM@nvt3TWf{(wia~}>+BbDkTib9j1?-@BCb$oPbxk{A z0raCm$ssn4gv5lu7`V}|>INDKp}#7;on?G7Nd(A*{Ib5LpB^8FQ|#?E;wjM*1cS{Y zU?J9e5tQ(nfX3`BWi+3`by6*q!d5kjK*1N2ml?{#57q~$#?jFkc%KDskxg>OLF4B8 zE7%)P`x5?9zbA;K@%R`rNTLd+F!=grW03rkySO6msZO;Y^KuSlDhWW*#S z+chARQ47H2V9m^KzLZ(*6CwzqpwcdadgZZ;FG*4PY=mTy$Zo$4$J!RcOW^CtyD%?M zV*sut35OMz8+8v#}xzZBnmqpUYU%zGxfhDDAI1hu}2)={}%0YxKZ5dh) z2KW0jL-UbO0|~I3zuSGy|JGaL88TuZtpb%9IgIY(p`XjtdKg%hs&?Ei*vxfVsV%U{g?>g~SzRso@&ER4goFYymhyx;?#NssKlZDL z=1zf`49~aFm>8Mm@aT_04;LWT5tTZVXL7yx=@S7sx#hJ7ZwCHMEoo}xzN&3 z4Z_28{|3(Wn#^#M%?4B-4sDDSywC(W6$5Yo_!@Q>yXO%n1|;v`_z3b8U$vsXbjzzo z)5+eRrc`1%RhflWHyf6?mKGW&?MmG3@mIykiIr4>NyJ!0zzub?L-r>)!~i_JyM zL?ciERyhyCC}%c0p;@?07T8b2F- zf

2F$#+8pt(Ix<1QkSoI&7n5+SO%RH+-8fF2XV1os^}C(=_Ph-9mNW*QvDYRu+f zWyo25(GiFd-bs&efi}eh0S*BK6UxVaZhN<_LJD-$1E_7*EWd|>3p70f!k}}5GO8=+ zySqCRc#qYMKo9<%|8;fxr5##0oZ++a@SfTjh~GIi!m^oba&h_Sv~p5YTkfH*6j20} z4v=VzpF}xqG$Q^p;;BFSnQwb>F~jOHmOcMn6p)XzgPb=)QXi^6K{kVBkcXp$%OgN4 zqk=v)oqay5b7dvv<-aa42aW^hEQFu~kMJ>`_+X5RK?U&^-b{Au z{V*&nhRFQvqG=D->r#&B8zpeshZ&bV^SS(+=X1C~xZ^8__1V1w+a83Pt&Jjrk_r|a zOw=zd_-GKR6byQC+3M}{DA4ojeOY%M;M^C;Y(h0#fu5h|c;6#&rG_Q8P)jwz4R<*U zL?K8$(>oAX34=TDGVTfL6i?RA|Ml;H#ErTzoxd0j7V^JCnX}rHabm38uaN6E?JKV_?tzMIV&@p}!NtP_ z{L05M&5DN;BDXp4uQ%@SV!GFEQedE1to4~q1^JOS@*wT$TZES5k_5A3-kvBox2RjBS6^XC;H|Tlm2v@9{vMAb0gpF?MaZbIIr)IV zGdWtth1E^PMJB7&Ve``j`i|m=a_+?RI4UY?_<4fO=V(xzqT4%XeK8qN_nA}tV_DMO z-Cc`{xovuJx@%^puC9iKjWz2NL&dhi9KhhLT*IQOdeNPt@_by=6VMq5ZjUaBqv*$) zhdhqueWe!|y>?5Tu>JvuKPt4Vdr7PqAz4H|C5#olUR0(2q?0|dfg_W>E~YoV|Mb7r zkt{|p8iV!p3hnSI^pj2W$)EnT1o@ycO#Tzr8?Kx4hGy3ZqSpD4X0H}tCXiMoU3H_L zxWG`!|&Xi!Vq8}VQi1tW7j3TEZVqwt= z7+W3+C}QZHmKU0vpu_u#7*?B1bLbnmafLxZlBqVvcslE_O$bGl6IO9djgIm^7YvXG z@;GkPn;$4eXlEhgZ)@c#z4`lviTAmVhWC1h`$lZSpqoXHe?mT(9vK|0rToaj;19AV z3WdcP($=Qag+uZdmd9$*tPenUk(~O{!Va1%k_*uP=4$g~w*DOa2_-VwzMP$#+X2|= z3(T;vuy0>gA>#yaeVw`fE45l77-;M4gQ%?wOT?VYvQ7DqTRtiYZEa)4s@TjHZP zAsq)77!Oeg2S)63Ylt>rD)OL)L-yT*Y@msO=rILr5;x>Kz*xpFs!%-U#u;E-_X@!u zFz`mbo!n|WKl{{4$_&}?Yi-2F>TI>4KrRXCJqWU4B+f3)hJFm2vykiOdIj>{!IZJH zjo5kbt!|G~pikh^7uYLk@>P&Yl#YmgEBLa$1!h}%ViRMD*gBc5LQxu(0t+FHhQpuH zL1<*=;!Z|_4TCV+-Sv6tl+C9 zGr&#(@0Iv0mNe_WHwK2~-#QTrJufY)^qf2Y)nqnPQc}*&&b|Xhx#;u@FV6(zGK7%p zza!1NDf}C%d zKM1pumz&O1zS~*HAg^byANAWiz`xiy7+%s!qtX3|oJ{B*4XN`py~O51%xuZYc!zQinL!XVrL^gNM9dJ53KjT{9UdQX1w_SlwCrT;lI{k zW~!(>9Hp6IOzYQf%{{)2Y-mDg)H$9Obadc|ey**Bz^(1*=n$x7trMt)#_kwGqr$;a zNp|{s=Je`3b;wIPbI_$7c<|~Rygc%S!#Y1($iL>_sdE)q!$JSGwQ|ajLCu9L&J*?y zOQr8hOnSUzj_d)0GW#^N>mXQnd%V>5H$-8-8NK}kti2KOIIwcDk#Gm_{vwNQ09o7o zLRYN%p`b~<4~vO5A%f>h)lyH11W(B+sry#*$u|B^d#)N9ILy|gChY|n-JvP0aKnVS z!vr#9zGWbh0j||1x#l#Ri)_8`tf#Z%NA&>Sa>jK+DdtmFmX2RLR)M-X1V2uH@*^JE zJMq+%((88$NVL>k)PjNuiau=a!GN-CVrXJvENl#uhY=6*-_EP`xHtHv5m$5Uvn+W? za0{u_@}_gVblCAe=f`V}w%CJYkMxWmDDZG_V3bie%s|ZEwf9Sl)7ZxpMP+&Oub3!> z;K-3g%%!HSo%F1q>vxe%F>TzrCBSIa0oH3aI(^foAn@=k)QrPEc=EK8F^tK7)@&^6 zL5`>~D>PqO3;qudVImSdydL}!P&51dgo5E%KRfETOy&rEDS)QcL^;pqu$GpH0Aa;? z*QLX0(S%2Y$%$Soxn1srLxxdiqW>u7gl4;~VksuJ_TwEC>z}~)}3d{01=5{0q#t4FN zvqMM}ja$-x0J5H8yGIofZ3G)M)1*GdFl@oUrRS==ylNEZeQ}HAzur1%A8tW~Y+WS~eiW{3kv)L9SzpPZ=l5zX-|`28PybT&O9vwcJpIYv+U%FcVIe9h`YzzSK};tLbkBZS^-?vM zV5B$PkA<)C^symZG@%68>|fyEqJke}Q`$p{A_Csu!&!zIqk*~(V9vqRM-Tz%8$_rI z*8ly{_s03)B=%aftE{4CC1nMMZ-6``CKdpW;3G+puVgQqop%;zJC-i{3G?>K=y6*I zTX#o`;1sr!1lG!K@yzi_EirL%eGjG_?}~ocmrzsRzBJd@yY;>J2E_06dp&A}(9tmm zUGzr&JG|QJj_x4ZEEx4`?ZFMm==Dkl2d_NCj@VZLC{{h$NW1)H6g933=*j5~8B@fD}bAloj)NmX1Q_X-S=_R7ka(Uc;& zu^XYwhpe=(X*sD^XTLkNswO}x9|Qy4-Q9)Of6sC)aW73&4b)D7icES&hAAFjz@ZA* z`6B&K2S{W3K(A$B0A^P?Y_BZtI-Sm(SOdKtsqVF%#JkrYC&MbEpPyBhu>gBZS4)lC zbZ)SD+TN(kE%5ZwuP9u+E9vBSm-lySN0j^$#0U8pEVFa19vB)Ce}z{+RVVjb0h?0|qJFw2_yc7s|jK%7`+t{)qn5*7A-wOWjzs zaqEs<7niW#gA0&AR`hR?k&(gmJ%^A8Jc*TaI1vX!8JmiWsnqt1q!T}ClSC`+p)_BUy#C$U*a3tIv#y=S&(>Om&$Cb6GE!$m z0p+TCn`sswAOCxCam;HK?RRY*_@uHQ<)khi7EVTA1TDwFj?~}p)(JVitW=UzWUcEq zn;ZSDw(8x~#}W~emc@V1h9|6hhV$AInYp>|!27}>JHIhPSX6dSZSE61IubIXtgO7X zrB#SE*a?Fx<@|TYm+2(r$7t9|AV_VrnX~=|)8ikj`SGP6SNnL0cmnI)yEeDQ=amw4&Pv;+8&e^(n?l53M2u7|cMrPh1Ar+a6>;XvBV z%pU1Fw6+$!@Oq{D zDEba_$__{4;>xe;)p)&6Dqs{8C$*{#bTxImOb6QQc88C77FcLU8|qFC?s2%d76HD^ z^vuo*PhC=&kjrVJE0~1+@z#ENMVakJ&VSxICMM1aJSK8le8?qI6K=HkpZ@!Df94F{ z7K^e3-#u_Yf+z8KJ>hdjy!@b;<3=Vszc4fC<>toT;4l^pV>Mnm;ee``cva8N=IZM8 z!ZB_Msugp{dSjyt9fR~!$g>sYg->1j8f@FzS{inCwr<}02M4%#xVHAT4W3hzlLJ)Y z5ZNgE2pi$Hp4tYJKYwE44vSv68ZHwP$I8 z3Lzm~+H1@Bf`6gk;S%616iUW}qhqyhhu7kmrg#*Jmxss84V@PlnFIxqA3uJS+u7Cf zCD5vKG~6U4l#Rw11^Vss@~y6xm*;nAdl_Q>Hp&(6iHRNjtmrb@%k> zVcvMUKHgsHz+v3Me0+r6^gqgS9@2+vbhNOvx3dd#+%9Av>6-#HaR5zf#(&-IJN||E zk-efwwR0mg8RI}->K(Qk2vX%*cHh!G;B`d~4?2YI4$xVlKeSnptW_)<9P5QuS2M5M zoka9)#rfTwNY*q{F+yP{|GWDk+X!*ZpBoAd|GxZrX9)SDoSZ_;W|zOWQBZej)Nu80 ze-)sPV=2hk$Rhr9zQLSOlahjKo(=8|iF$hCTI4!jauzXmSg!@6>4pIfSJOKE&flqK z{=*@110xYoxHL31F?oi#zNNEeqLB&~i;sL#ko7|)VRCF7npROW*%|@Xs_CGq$@0>I zQa1l2Forq|tjq_7v&3^*49mS^7XdgaBRwNz6o^`^LH!bEDXz)k@MtEZvcS)wr*~S} z68WO7I0>jd7+&COgn^A~+`NwV8#>;g)g&)P6+6M2U}rEk&F}uNy7$l`{Jr5;1Q8)Y z9=Ii4q)>uU0#63nMezIooul4K9rv(|iN90r_=vlnWvOfXqqz9LU%CjV%XvCZI_N&d zQ2ckTz{CXB=YD&)8}4qRz65f8L;bgc!NI{l7Jsg;uS_BVHwqk{z3lAV!lUw+Gi=wI zPr-a>ntH0!X1h?(4h)!C1)q7!oGBl8Fb^%4D@hU*B(&Zx-`5`mM(ql!OFvN1vAHif zEOBQsRCnw!bzonjffs>?)~RiLL7^V3R&fo2xR4_&q;Dc4F&o%D-F7d z#XtLUVHDVhNUu9U{7)Bx5A z);`Y2r~KZVqUCP|;RE~0$=|?Jw@{G(2^d@8O<7%H*VWk>79QT~Ix$AK>)x)A)Qb%}B`>k9j+Hv7xx=_}yxh9p~f#VI$$40AA_DXNe1hw+q}{0V4e`pzT#- zlsFSYOIr_~D3#qIS<#0A-usL>mtUs^E>z{*u0T^FbFHfP1;r^<`(fWwaY4EI=HgVX zc6N3a*kk-YGnlUG6MGr9c&_6ksu{0=KF5{DogGVwsNwO$||-S^z9 zRIEk2K_t}Ue{sGR8wvEm=#f#?qGUmOdPY_bj_|bc;_RK>-ETsD&v+SH1R0%lX;NMB z8-K^?$dO`%dhoLgBx{fYs9Vmy8m{_ZKNX%yEZ!Cfy>%;82^f* zj?Lj5wLCane5)#%Z&$S6r{-v^R#LQmz{Wx>f)%pvv0|!3{S6&Bb#lO*8LN?!G`;2* zOiD;d&-@{1XV7&+eD4|tzvj61IT%>C|^Kzk`o zu4;L8)&F99-9n|yeXc|M0t)U9lm@S>T3S*x;WjZp=4kr?m)ZsxBbsn}w=JJ$lT@1) zJaD^)-cYjyIFPD?2a%ADwEz6F1*;BA?Wtn3bzv`{e@Dp!NlFZ+zYE9bt(C!1aQYhK z^;j|>;NcX#`O)j~$dd2vCttc9bL}Nk`Wrbs_q1|cgMNtTP-Mwwn+wtE!_%({Ko-kp_Y>HA zM@fY*od|Wgpkw@w?2k6G^X5kQGF)*QxX|68@LMTy-{G>FVwh57D>dVXD zG6l({*?el#RQ|u8{AN)k+aw!m+Z-OLbJq#0@*H z`zd8YZuHW#H#^)eP+4HDg}omxt6K1W)Oe#!S1ES6Lz?$Cg-jdyc(UjY>(if45VjnB zjlPakI#4KFf^c&6y!z1 z0tFljIicf;;|uJDJ)R3C>0g?^PAR|H1&}Ye?TwoLNYia=Y%l%}E=)B#2;FO)V?p`1 z+N~L71al)p%zo9CeQ3d?HxM^R;=h3%Tx-%@rqrU%G$3XDTS9$)=9JD;XH6& z%zB;BYAigou+H8F+&*LkojmBJ-{wC*|EeH=$iR7K*d&bddW3%rK%ssux^;R?4Ymg; zXc=!pz24DL_;R>uycVY}Mz9(8RrQO2AEFe32Qn@u7FJCEQB%vNquZ^kexqF!7fki} z2wX|GBV9VxS9>W*cESi0b4d}4!vk`&6fn8@lc*lcI9`1wzXhA_=AB5@0rMZjnlrx| zLN{gz2P)`RZr0r5@^V~a;@H5zw-yDz2BftCsSI9se*J;K%mL^e8gIcbbC3J0M}iMx3#DU$^tid&1oEmbP z1G5fB8ithg910eB@JPD%d2b90KB$fgn&x7w=YDV2oI2gKPwtFL*JI~}Kl;9%&J|j| z9mG)X@9e?AD>d$SuFw51xGyn$NH35Y$Wp_^D0#f}goQV)ze2>sCX740Q{qh7N5>~6 z{M!9=zu}>p6v)8BSAs_(4YM##p5&8~dJ}hK8|5e@#Y(5N3)tFhM6*H~+|EeBza{!Z z1&uU9&B%!*MT|ij{vTYX$LIcz#@d?Rqt)=T_E?y&g=4D$i-hECDZ3^j0lyEr&Dobv z1pZgvmUAi`%e{EK9-*EJFUnuSUcdSI`GLRh{^3Qx3-O*zXQwB?$A*`;e4wNhg4CKF z?ac)T&Y-q0%zaBUEoG|T+|SlRocek|wHA=uA>`2KVf6}fbHyYK48Gpe>L0X#C{K8J zoar#glA*7H)=)_FUibT^*477bv<8aCt8B<6EvgyaC|5OuPx;D=pAzC9g_AaL8P(HJ+ zpIwqC-JenKaX|j`*#r0<(F8KZq9b)* zMn0E;o-gtJI)r?m60lP`_=dsrmY@ zS7!+xpIc{l!hwisc23U7y*-$YuBk}?$QMvv>g)u^z`MUr9^lUoPEP2?W0#kglaq?) z*&sSwSSZykEZVjxV6#824-?$s%08r@7v%_H#{y1s&j(Ys#}U0Se8!zB7uupaNGr3m z>>@1jEB;S=V>E{`ZWGDL$*2FWJ!{u0v_G*#cQfDh#~jYI4XFh9P8h)uE{^@-x+MW- z;*RTbtM~pF3yZz`XfGch>nqY`6Q{?d8$kFt!S%U*U~f-aK}CM?JU`Wq7Tdo1ceAOB zY$rP+`TFGNr$fO@r!B4u@u6KZKAusla$#X^aQSGpiD+x864?5OGg)V=RS~77qkwx1 zi$*}nK!Jfln*A+npk;Tm&Bglp?oWZ$n%8T469NJPs1U2E&4E-2IAGT`HkyrF99jt4 zt+Tm~)`Dm>5IVIqk~OvF(6#_bJta96D7Z0Ra?!gR9S=Yo0*t-3*4991O$|-kKVgzK z;B`W)oNZ@sK2f2+2pAjE(xDX<f=uRdNFTOHaLiKHPX&uwi5X*c41+ZNfFassRJYY7jWP!j4ESjD3ozia$7)a9iBkK5(DxfNH+`((k$yI zw!P~xZ1Y7Ju_oii!N6eN7XC03F@7u`@DFX0D$9hOU-$q0Ui)Zb%B+Gj^v%spK=I}C zy+8Pd7Wl(}a5C!i$=v?QyqI>4VVm0tuC!<+y{4L);?+4tsgLBh(ALn<&}4A`a6>_< z1v7=Qt*v~^;_}CtLv(rBh14{C*l%xdL?k?>PSsmSwKgeT!NHwvUbfBY&8c^DG!wmk zd}&EZ`K9o$Oy@CpGk^d71tkV&$BV(-q#b74xFm3MI$5kT?lB#Zfr|hF*Z8Qve=AyL zM33!K)mpQ8M09k0Ybl`r&=1p<9`^>l0&U4O0k%ANot}wl5;$_q%n&XAF=N6dIHaWg zibArxyb+KPu1Gtd5MM6OvpVLBmC}+^vpGGC+?1vY8Pt)9cr(~tQh(=3uBUPTL>bk` z@9piaHT3a@h(4IoKjO9Hb3LkMF%)pQn36ont1iVEW#MPXCEQ>LOP!e+Y>D;c%#{2#b8L+1H}2Jki!g$Lu?ULF?uh&%F8)@P z*`if#M1qU!{daW7d_=i7>}$$bzq`{vKl272jqtZ}O`)tHc&tpj5+KOHAV*tFbg(*j zHK%<2nw^{uFQcWU8P1A}fuXCeZm6`E)BQhMfVzGr*skhOb}XiDps&r$6ujyE8cE-^ zD6Qo_O;9o)gU331SjmZlN$h(!NubK!elWQ#=zm&Dsf@OW_zOJqzqgsTY&IosJwLMO zw92WfQh?(pGcyhq!EIV{;^g@Bu1zX%%(yo!p7uC4CR_M1HPZ1e&GhzmCiw5cs39?UnnOd;S<98@VPDh9ag zw6v&8o@M|a-674+Zi1T?+8?6KK|!=1)~+ zkF=lEAd%iPs;jCpynk<|tx@U|3Wfrd^kyT27}DvU*b4e(+fks!&Aqn%Aq(U8vobB+i$|2t`+# zZaj6pRtV_mMgeqU%b+9x_8)4E-oQtq-i85W zG_WXQwaPpYc0k4zV2e_dlg*gMM#t+nMIc-LBB5DWU}L9?{pO~D)k&F&sVZC8&p=0m%XwFd*c7CEWn`3SKjH^m?=f_H z^pH1N{OUt|1J##Jz}%0Gk78kF0QmD`U>obh7S~kf``%vfM}4E^{k)3(kyr@h0q^e4 z7Hq#J5R3E=me4Auvsq21#}V;xxVrBjr+lT76>kZeMrQ`xHWCht+HBG;U&H_6u3o+` z6uqLnym&9cpM~BFl-K5}7$`QDwvMi@%6$At>Wb_}#iTd^XD90Q_aQ66L)>&0=&Rem z;SrEBGPtc%eJv7Gzu!H~4qW{netziWw08huRW92_yYA{T4`vG`C8du22}Z{E!IBcB zBED<^o59!P*Y$Jtxw+(j;R|fHXS}=oLslT|X_#r^s+~PNO41ataAiDWbeo{wanEpY z{q;KJH0_r&CLf-K#l<<1-9+XGz_=nh1ucN zgB{7VC@i|51eLDdz=ZTvHW#27qwaOMu5d)tz4LqZ6$=CV*bjnV7D_=_Xu@B23pPmT z^M}vTV&qq{8kjRdmTmtDKzJi(2C!EE$2*)cR=IDJrz=taMyr(cP5qtz?eU7wPVnPs znA^o{h<>;uvjsZig0ZF>xAOab-omup#s>Xp#0fbCb2>F>POr{z`1saPKCaV=v}&v3 zVpq+WXq%%Y-8mxPn640Gr)Y)gX#i$LZ%eGmqsZguUkQTmgo6R^2q;WUibAmUqueQn zE&{sN!9&1081PW7(r*F#Pl(4b;)EnPzZ$G-c&gU&$y9YQR86HSmruMw0P~0C!r|!G zfyhaUukDs6Jc>b}`{uOMhBSl$kNp-YzZ;j@t^g0DHGqF~9Q~PhX_uj zcX^WmW1tc){IM$rol0zNvXbClI3BFSN0O6s{Ho0p>9{$$Mrvw;e;OSd>+6wB%08J8 zI}HRO#N&+)-UDA1H3I|UbsE1{nF=jtUfad|E@_{Id)cKQT1!|k_z%jc?!iWnhsjXCVi=Vp0@ z=!x+Oas9{;w!JVN!S67d_04s~OGWBh!5$2ZbZBI_AL@X8qN}WV>b=9*FN) z?k@%&+kGFeUmkD3XmVb7VPO%EARn=SFElB8b@Hf9lOq^i!j}w(pV;2UGp9w(^hXRr>H(xTj9m5^2@9xSEsuqy+ z!6-tepa*GmpD-7%Bg`L!F00ka)07HZpwChKS9WL^?0uKb*D=aOY{Zv@zJbzX==XKI zCrtEr>uj=y2ZE`hE%xC@;yV^Wgb!uW*E6gQ|I8MFUb-kDgPiUtF73$+!kR#s0#_Q8 zhH>tGfzcRos9xTkYWc9%Ki~+^?sfr-89Gy1C?{x4F2W}d$f9At558P<6FY{Le_ULw zbu7HzN$53&0G4%`cmp4w-C!sy^J|r?iu%pvj$~nk6TP?a{Hl%WSI5;(-G&2D$cSig z0fhi3O5f%3@o^B5rz{RJ$SmoI_}(snr6}(21RmK9R?}jO$ZBD_wFD5JgPEyo%$)4( zLA>L{SgNE3b}$W@!cRe*lpMU&kBX5O^CdaaS}AV!pe@nWO|FxR7VsNh;W|ESe%J<`er=M3g_Xx5)&_Jg*sm6eqo=PWN`J23P7 z#**ze+PT%3M^~u!qTVq}C!9Pbh+UoPT(5a-K%{DskOJCv*Ef7BxH(euR)wW%X8nwT zB{nX*A355IiRd%fAE#x4LjIYfiD&v z%kgEi_7nuQ%qAs8*em=v#i9kcnE_&{O&tNJw5m+HIM(0L+u7OS{E&#d4|Q_&6uv`m zZF##OzIi$lgDP=p1etZ_gO-FiIc{}9Rg39{0Cn@{;?k-s)cxUYT984IKt@)@p17itgLGQf=`W;z6%-~ zk`X8)46w-fwLVML{r!#(v=Z88uqMP7R8$3l2|`m|L_Pj zCqG~Zz1s$ZqvRl{#J^ zpHOKPwOkHoJVXL^fB_Hmqu=|#ZM!|76+M7#;Cm>Yj5lUinhgqQoIclITriY8&vqzc zFC*Ey-iq07yu(*laTv9}n3HMO7=*uRYinDJ)bAkRV(wx>!J>+VvEQJP@Mx$joB)h% z2rR0@;=0{Zbrxgl8URwK>AK+|T%eK&q7ZX=j^#@eWDu;i*cW-SgWau?>v)ZhReuzY z6Z|i^JbWtKtWFU1j_`|)E1d{5;#0`{YOc#7zh*98J0EZQbVFd#K&UEA9d1tT zEF9euEol?t*A7A??ep__U4OZ*v>lG40ks&YXeeT(Ul75rx%`b-Lq1UQT_*wJC=8d2 zgyF4%{xA68apV#-xbq!_Or*bD63%l3tdf}`QW=FmJnhmbq!)*uM9bZQXe!0KH9xO1 zR5@MMtDCHfR9r36^KC^n0Q#`Nwu+M?;@x6Ev!RI3cdK@pb0?l8ufh3aiuOK7P;-K8aa8SLMufU z+Ox@dR-crB?ZG6c^^D(z(#jUpwGw!=tu%hVF@C}c=HPR@*!08CK^-_SHzDVZ8-nCy zGt)mfI5>AW$4$jmyk(o8yU=B31opVdy`?7izdtD%)vU&#)>d9#w%gC{E!z1na7U?U zRxT11ioNF*|FRA=s!pB7bUNHm19=Di%Crp0Gw+^mPr$Ki>Gc@SmLkAD5nv0~+x?C% zciO3N2~pU&2ZqDDyZz5rn=CJ+5@KV8-1p=nK4#_CrVEd1x3+a10}$IiEWFM_n9JeL z7$wU1VAM8Rst z!+ezAJtQC?SQnO-mIgqlVdHWUPj7F)?r#TP*51|c_N`z(AZzWMmx0~Asj;bhety_xCG{6^!*nn|5z3kE!4cbUl5G~ z?Mfe?uCflYMNIsMudk6GD_gSuVFFwJC*iuV=z)jM#D6)MHD>e4@E9O&J3rqi-3(n` zIz?~ei6=i%$tfx6b_~BQe5Qke2?Oh^5olywj++unN>!WvKo@A#Z&uXMm~3u1yGQkW zK44Z-Vy3Yidg=59WpuDm*=`vc8U`uqe>z`s(O|7yV4e@o;Ehd9z^VjX!_u0qQtKKc zyRpzvXl&pKC@Uxpj2zBnOH0cV67sOHGNz@Z?2`g6?B3~00~ypy8cP!+1M}Ve{l!KJ z+LqVrtEfTSmo^am0SADQc;Y@%&`ShUu>AawUC(#U3L^f0OY`f^084)eFfU*p7XpGo zAoA#veuhiHWoh5&(eY|A6&S>!6E8F^(6ITrOd@5TkSi{pYR~xNJCT9*5>sF z0de#d1PT?97;!Xl&LOD!2Mo`*YAVYs%d5d?Aw45&4Lxg)prb#yPozVd2&ni1*anZv z>0p$)kNX)WE=HMHTXZx4_Hb+dvUH!w=%?2L=xie++V?EQpm8p|!oTv6rx_9Ni-$>q z;|3;g3r0R}doy@LUJ?T;x-kK|{Kt=|PLufN=|H}}adPmfMY-4o#xF6d)^ zs|pT&>6b>bw6NghR2sbBe>eqzYvq^HxffQ^i||NMDwACQ15&yL>&N(R`7#AP<}JxVMHg7`?R41K2T(gRNo^)7l%HbbOXNpw`geuJZ5hlYj@ zs;2thPBFBqby(^}%_j2)obZeGh;2B4&VW3th;V1b)DJ~A|^V*c@HBv{) zNJ+u}`yKE8;=6DJW>p1#nt|-5*-S~Ratto>;?D(Wl7mB>(z1XoxAUzIJiL1ZPL$Gs z@rANwpbW+gSNe=~i*&1(OqsNE2nY!9(v^r0gaZyvZY{X{v6+(=dfXhub*>vw$kWr% zz^qr4EQx6-N-1h6e$=W4kK@Ta6A4v}uOcEB0H1>vbA5I7XRSmzAzS$B*8;=0BL1b| z_<>1KS^4_oN9@@n5}usGMCTVb=?kY5ShttW8ONDTzdCky2L}i7zCKX3Dm@NTrdiG{y_f3x&M=#}8(3%2wXRU{gR+nTgylflOG>K#hrt8R`TNr)We>!Z*16`}fA! z#_H_N^|qVzq;%35V?I$JyGIrPo*Uq;fFh%bS~yy8&hlDK&}WH9$1lr~x{a-^79)4| zph!Coc;JO+=Q}JEv=9iKmgCq5pDyA53qgPTMudyI3Pc5pY?+F*1h!)~I50&rGJ8cX z$kc{B+6#VS3zLoDZh{I_ttMN2LqjUy?59!i2*CF2r-uI1(NSS@zP8XfRky1I1E=rR z^|TPv7Tdoy0Utnpa&2M)kh{v9)m1TO^MfT2oSjuD==%>hI_l>zo558+`u=RqV|5JS ze6LEMmx2rt5lIDwk&#gaO)pBA6~AwB5%)Lodgu-_UCleUyz1(eT=9W+?Wz4_@2j0x zU>OsmD522cSo#h^^ks5zaHJyueNxzDTMUH1i}zZueysfo7aHp`A(lz?Wx;8>AV!^d68d+I&S#yJac1p_GsV})G>&)0ZN$dIBeH#T zbarN_r#9E5&l`O>$!gf?IXsyG^26Zq>hpw+m0TJ7qa*@OadCC{2|zV+M20}SERY#0 z&GM}Mw!`Br)RE6;+8~{2FX=3I#$g+Xn-5Hr?lqfTV7x4qwMD=26NwbWFciFkngJv# z1gw3>-Pv&bvrr=2h}rp2Au|w%vgIN(>KMmOr_%pnbhZ1AwK@MDX4GQR*48`$R8|mn zU}k1E&hgm(uF0s)!|{nL;O)>m95$lU|3*(wO&;*gBVf*ePhwIS78WWWsZg67D!eT( zqjkE~Qsew>80raakY}A;nug|_hO;i|GSVrQ1kc90& zxnN)e)9Rfjj;2GRz^k!hQ6p&Cf)G`^!W77U|$B| z&VBl92cUckdz{;WOE5qV>NnecxH1@e7Z3oi`2r_)1@vx-_WU;R)Ztp-20_-|CqX9) zUdNSS8A0#AI%;P5$;o>lyUvik#cO|zT_xav9Y|WXWGGQVD(H^7#pmaq+=_z8gqqqmh|@wj5&)wDNk|`(RF05QE3U zWL$^ovpsZwwxq1BeX;w9WWU|3(1DUHb|_JiK=@whm(6>v>UMQnFz)DK#|} zXvwonOT27cq0?}(mshIp?(T|qjd5{cBu?OkyY$^Dc0L?rkF?ny;EnSxEXjJp)5eT7 zS9T)Pu&*5KSu`{T8;kheVH24E9WUGoMNw%I5MTqn-nN6F+oiBsPKaV*&#R4 zjh(`_WrM_X`)Omz;6}>L=}x3$aJf0yblSYRDfz&IM!)tG1`7{0JwB8t08xd@$bet| zaNFKRbi2raWzU;cP9`B_7ZJ|tb)z-{d@i$?olog4D+&F^)!0?<86IyxZKKL5MANOK z|9SUy$Bd$qUQc_me#ahKOK4*wk8%edjGph1cVFbPPl)4&!Gxi6Px=Pl$%cksEsE)& zkqZ5s{nhlx4rvJmLx31XAdR2=MT%HNkg5T(QPU`1c$*eT>7XG#mChd0MoCO1B0@US z=5|Bdh-^WHoU2+O0}_2F3uA=Dg{6h`%&*d(60gxwAzM2?B;?F`2ee@%ccehhdHpdE zNp;7YiNIw5Jncc&*9~Y%%=#_6*xT4TcvFoyDM#|37$>>x9^FoOscESVIz96MATQD= z4+T8SY%PC5G`>9s%EYxetl^U2porK5G2;4tEut4czvn&}-Pnxia=%y^5GL>2>dnce zevktGFA5GpdpbAlNM$m-UY&ROEd$pLnL$7R z_LPL(j;TA7f@>wDYS^1Wf5k-K=l&EH#Z)aKCL#)z8uItN;{t$G8#ZSNQvFp?QPELX zQhvnGu3VEF@pu)@#Xr4Gx}n({db+(JL*L%2_In2NnLN~X=C-!Wt?Zp+V2oT!KaE8{ zTo;?D_G4%qI-zB-ZsIxHPpSwsG$NkUsutdZ$sg;)RlAr{USie=QBm#_X-oDwKKBq^{l|6`8~ATRgBU{NNQcuPhaX4Ei@ShDgq4;215$GoJLJIZh_oAXI6EnyGMM|= z=Gd{Ti;1ZR0~H+}^lTwQfPuE&>72Fu=m3yhX8HNfbs}{u|YGCT`?Bz`x}1 z%OH?zTa*~4rKY84@K5o1;PfEXWpxxl(+A$bqXu^;RmG&?$CU=kx&H9F1icCHJV?#vu^IsYc4 z{8?RHU8_pGw6p~Galc4mx{QHgn^ti`ksVoe^7wC4=M5;^O02`JIn{_Yo&KHP?)48< zwY)~W7F=@=ZwanA`c=VeM>61l^wTu@N8QLfij?F%3usmqPq0`=M9d?u>Fo0L%k>xh zF+fUk!n7Ass18$o_ zF@FErr<=q?lSzp8LKkS!w*Y!02l{lM^jQLwPhEz&RpR`zn-T?>U{%8lP z3)cJoe3OLR8`)R6HmaBL2NiIhuiifZORpmn@pWZ3rBTTxb0d z`cAL|Xmg&dHtA>b%;f>GAjLw#Qt|{q&Vraz0g>kT334iCu^9dh(O-~8+>I)!{@**l z`TtVLpwndBGYi3Qav2%XA-lmPI*F7mdfn&fXC4(F3G5yt?^5~rF-Cz* zKWplK8UEl#1S%28HX?d(Z=1i9`~ZJgRASxfdxiO7uJeb%1GpX5984Geo(C5E_>WAq zPiC*~*&H0*c1FklqXpR119Ps1x8~7iG{3l5Pp6PnAsijB!Sq_4WU=ChrM>ofZ)x+tGX`v?2TxDRJ|^E{5$&`Zre>r{&7>;_u1n zft_@i#gXeune4&>w0b&wnexW%zMq#bP2q*aV}8Z`zn`$4%yAE`{V6)890GbC3?RMUj0{nt22(PoFH^<1&NqaqQ0 z)GBwaJr3;I?<{rEC*A(FLr@;-R7v^Hq+hdoX z0~!7AM!ThgOTz)UtXSSP+N~f`FuMVg@$S_P0Z9B>6rQ{ys=;(q^T48zLVgWH8o8pO zruLfB45=vaC(- zUAAdp@aG_&Pd~SnP*EkrQ3?1GyV(&@vrZ=P$VB}^3sAP!4>#7Vru6CQ>32{2J7Fb^ z^>uXen>BH8oTBrJG4jCi4$+^Lrq5NtquKJjQj%W0Mm!`>vIkX^503~0irQ>&5KZRp zoIC>lJk@teKfk@g=Bl%+=r?hAxVjD!$Ik{Mqh@-Kc!J8kfN6hI8ShRo1>FoI?26mz z(JUw|n{-DfDCn3wikj`wQs@otamcO`N93eN~qf^CG#aa&L(>Cuh<>7OQed0g3T9!79b*--x!Om zrW_APLP3<_V}ZZk;juI@fE7h?EZD08c2&#n83nRturQcPa@*Ljp+sSWQ8EN3CK%aD z2GAir`1rFzp#xc=;^WUT{XpD?I;mJOWBex^9wFaLaWP5Tx6t^k;d|$>RXwDF*H0byB~@YCJMB8t~itk+Ue9HchjdUvrqP?QhJFQIJr(|OUph! zUIBGjW5BVeQAnfEAt1x^xIgcrnMhUp>xV zEC&#{_s(3*F=R_OUvoc8oENvMwQZ@64G%sQbw4cyji^dovYnV9XY>Hm1Tq{Pgj-V; zI=LBx%DO0LD;aN9Rrbtj+1%sfl#1=eO}{7d(1p^keTSEvaO4i^>hl*HJ72&X2^cJW zC8f_eg!3RI_z>nO>Z_y7>#+yXCg}VbF!f>oG`?|+G8o~N6c;Ua>8^l1x$zW6FsP>S zZ+hwnw%^S{=22-n!Z%vAe+an}|IjY%?F2c=DAt2Rf>#9d0xlU_^W{|Ey0)*t+{kb& zNrVQh1@@7mROk-tcXoE7It)qJ8gyvyE3=F&k|n=t^&H5B65FNoNoKorlkKbh!^1x7 zmbxYijPrnCP~z`FoAl$|a&dnXh0t|Jra0Elz0*HT3ltN(jBrSKse0J*DnHV`!Wg>g zp@)R*gYuF}{9l8u|GWLu<vGr<;8oY!GDq?YLKBVKqb5g4RW6U#(x?S{Un?Fwkc! z7{9^7#4-=n zlri|rkGJl&V*8wn-T)Q9`+l8Hu%_1WkmtKiwBne$c20W>#~$@8(YY_(ZHN@R)MAJ*QiZ{`<=X?zB`;Gk|30BC}zQU4L_E z>(LHULXI~q*PQy+#UA9i+{C1%Lj3X8pEo4qPn33aC5JNp@>A2yp@Y#%asb9V_AepTX}xiDN7)7O;JiKHYaM;Cf=Pk|1@%t>~BJ7BaU`eVyD`T_04ylKRLZdv*n z{96u|tC0-uyVn3~Z&$IXbl}L><~_~E@S%=k#`$?{O(aWyAPnmOA-`0)ERM(~c-v_* zX%5`aKSzVl`sF1+=;g()7Sj|Mc0oQbH~U`+TwXo8X=+|J+T>=hayhy_zS+tVVIw6d z!7`4?DR_4x!Q6IUAxRqinV$EM^wFmun*ec4x+2TG^zZe=%h1Hojt)6rk!w=@O>t#k&mBw zfVgT@Wh^#PA`kn5g5nfvyy6d@dLlD1B%U90^IE?aRaX7~YIUS4ZNf>1xZ)}v$0S9< z5pd6W9}w_ana~jsL{W4xKe>Glc}>`O(Zse`x#^GMw5_!MylF8hcH+Wofbm&^Cb7TZ zzwZV<_-+hJh1q(x-cOE{yUj-~?@j&))_P6FO-n}mV_D}hWl@|S{9|Dqq02$&Z~NnN zhu1gQcD?c6aBJaGpweU+f4+r5VRJ-V3=qfo7Rl}J&vK(;qE;{38yzliq*`O-$IE-% zMxx~>o|V@SH!K$9h*S$fXXPtvvWAf=LVO6X?0@bmCOFMW`^(Gd2=GznwK+?A8dYTy zY;Ps+7!D0dSyl0)k_=)iD2~+=G-+wcU}An=Ej+J6$wwF6*wuHTJ zcXsW0X<&3)J-5kQBj7(@7zwNDOA=JVm7pm?qoGnxv}b*AH`?(pdDb=*5_iv72{VV& zR|kI~NZ7poV8v)Une-W7qV)uveVXz2dJCek_}2GPf^T`whwd}7(NzvX>7iV#J(mcQ zg618KmFsJw$D?7gB_5NSyP?te8X8(KVIy>f7Q-bL;=E%Mqm!A-nQf2sOafztbu8-0 z$fG~#DQFl)7@)Xl=omNpTPgl~te8Bo{s+f_=(`u=_^-SI3H|@0bN(;*68cYCJ~|;` zt;t2U3k=CV83+aBfo{mTHV)3drl#id=*-N>7BG~-9UBG?j)s;-Z7Znj^%%fd+Mhv% zYpF(GUsYB0zJl}PM*yFg}z)Cvn^o&bU!9T%5}*U|6x znEMe9F$5N6_t{iMHNX*cl`mle4fi+cXL5BZus$+u7mhRF#(( zw(?Q)@v-vOAb{81-@4>NR~f5==`2AOgHH5&hL2hwyG;jRjR2=f%%_*0V=(z`X}SNOZ61?nda>EY=|sN4Dk36+VY;)QvX8UB z-%m*iE?ce5`e+$Xpr_T1&moE-C%HW**(-63c8!%PfU!N zcitVZG?cfGZ%lRN(r@K=(Fk^ig-L(sV%M|pv07hd2Ll@TO}|H!;X{7FqwJHC5g#5L zXsH7Bjhc=QnE!yq1mgzNhYW>5>6R8$v*(1ArmLrnwr!`TvdcX!p))ojXb zMSLF;zh$AwaI$e(M30S+x}LAQO)l2~z86kfqCtbz#(@&**_yuV(ef3jvzM2@_YqT4 zngZ-4hqdfS#pT zGANPuwhm2gE%!f*aSHany-h!a{C#Khhsa(;g}na)CD-3=0viu6gjGOKKPDPC<+4kIhy1Cf#0ubZlc29Uq?^Rul&COScL; z=Ld_M`CZA$TsIisls-CqWJUvu@>4kFE{{GC=haqr8P2AgP zhP4|H_wHb=Kzk;YSr4C^r=K43HA*26np!WjCEl}6RM3n5ZTf3@jagx zt40#Y+B!P4H8cQTdGpupehi*52^og!?CFnFP+yo+TO9(Qz)BGheeclbt?G@|Zva+xM znTYX!18|=Sbd{Cw-E5bDU!c~5dbyH7%zyf2D{Mnu%jgYw*MPaOceqaxe)&7+_w^Yt zeGLs~FKV*@oQ{EFM9 z@2abT!umn?gSw=oqCx(-VhjZd7Ri)9G+bT^8ys-fco`X8_DWSv^YW~x`T7{0yFG(& z;IU_D9>Rlco-P2fW-~QPZHD8pyI%fHk$#8L)dHA)5zc7*>raE^|=ntAP_g# zGZg}Z4h&R)#YkjS)NnStpSch-6&1tyG7!CMK!v*e4T7=cl&##d5u|)arG-*hxP=Ca zu8ITUPrlj_%^Mxo_puobfO;bAvpBiW2>4h{-XJq^vRIwP=1gfdT9Zb_8I6RQ@i~QkHk{LX%3Z)uUL6U!mF&yGI{zv+Bg^~2y@V&=OWSYNePfOtGnoLn z)w@&N-5pTk0$wkuju5rQ`3Kw{r&Lv){gzFTmyc11mVaNt===i&qX4M<{(L@yp}4$w zakF3Tb!`HU#fZ<*%u2xLj@WKsWUKS@JopU}>%eAx4<`t1YXKG!W7=f0vr?F;nL<}0 zAK}$yTk-|j`Yxxsm9vEiI4nvdBAz3^QW11OnuCp{r_PEDHfPM+obA(7CRVx*|5w<9 z7V!V|0s^w(A@-fRo?f(g1WGI}cKAx*$k5PdbIHFbSWr|o^hVoiQaaF911MUHf~h@l zlNDfISQcc!3L_H$1`8OP8+GP%2Q8<0;A9;12}BLnx~yl~8mv(W2`Jfu7^bhb#bLZ) zVuL_A$i2Q_T)oaaz0$i%lyc(7n1z}B%Cbm?kYf6!`_U!lC9TXS z0d9-^R|qFCy(mLTO&HmztV*bTDLycqYvyvI67wZ~OUX*hv2L+rI6?~$zsV%#Nli}O zE&xcw;4mbRl87=5094p<`B1(i)wmv~Q_2CV-e!;*=CPTxSgc$*HFa}!T{r476Ez#p zVz1Ig@oPcyW42bSk0oHz-&$WDp7c zm6Gr6jgmfndwUxfAC=8vFJK%r8W{;1fB?9GxhFj|Wa7nI>dnJpim{+)O?H>3 zQ-X{>%@?C6N=Qm#b@R)?{Tf*I{I_`lCM%5@K4`k_^oRNOz-;usfDY#r z4>|qCL0l#Xar_BGI$U#a4$JT3@jV02Nz>>sQ$A4dLPV76IXXJ(*Hby0NMi-NUm%US z)480mOfUeWQYIcG4j!n0SvX_|!9Mx}zWAAwitKg&>=w39&^`VKzYA{cp@O|AUWW%- z$T>&50g%uYBfEdy{mg7}+*DIhBNp;xLli4+;&BWq4vY(05L(}$V2I7+u{_J$tb2j- z?7p+x%;z7F6$5-$^kK{DNT1TJ$|O&eTCe#75a~xI5`pyd8ol~?%I~t;WSannaJbeH zFS&LF6;hcS8f!zvPuJ@Tnp58xU}4QQBOg++@xaaxrXi#(HWO+L;Lv&x|NVS)F>@>_ zM^D9RK9jux#NW?g28kH4qiDhe>*zCYi#D@Iv++SN5*zSPUck#hR4w+j2*PyJvgIUYEfI*A;W zv+^$Vg!tTwiB70uBZn4L6^=qwU0N7Ai^Lpa@32NrN6qy2SHD=(QTBMWyvCQfEFDu{tp3%%a)jJtr>M@$XT`RqIT@(Q?}^5>Fv*etr6XxO?lb zth;wzo9^yT=|<^pq`SL21f;uLkd_9K25FIQrMp8wy1RL&>$}$4;~o1y*v~JFXAB)G z_s#v8^SaLSIHvw0$Wlhr4y%{6;b8%44?a_Zj!7XIzx!VJ<*vu|6^2=FG{qY+41!r= zp2|F)z~H8jLMLxoQ9q;Z1pov>oPn9DmR?q7sx81cc|IY;PAuoofJ7z-0;b@@5?bRs zF^W=S&1b&kQ~=g}Jl;2I{{Ui^j7$QDjgjy}Go|mBkNkTFM5gfuViBOv}$EP;I$3g_Q595#_-d=j_5MCG#N) zjjACw5oK4L2(;3=0fjDqf4@hNA9`N`%Hu0tZ>1vjk}-Uqd=T3p<8q}pY^Qfdd5>t} z;bCUFZTwVC`5~VENQjHKm1Yo>;>~se;0*~>mS|LldCj@~ zoW&|3tx8WUC#EM8|LZIA8G6^7nu2O2@`k3Sx^f~+iJMyAkEc+}J=rSB(de5)DsIHw z{4N!d_4Sw#a3g2ZC*glRCs0eI4S1Ib$sH z#jznyzACM1Z-$JEEP@p?039d(_10+dINzLwgFPiH&Hf&mLSwV zrGo^^y-IQo6h5;Ss|C^4qJ|VLM`E7p@v+}!^fen~2e+WZHeQ<*NJ;eGcFEXr_BV+f zyx{_fCW52qJAU~QJ5OFbi4S_}OD)CT;p*FdT5=+c&|I|1+ zF%vXmYNZ;3j}Cs-*Is=RIeyQbkv$Ic#rmxVs_S3oF!lvw`CS|?>;XiC6ThIY$kU-r zl%p6ryf1;qusk!9a0k^Ca}t;72t$^G)2_!;G|msYcfe0T?0qTRL3D;IXk{Xbt<}%`5cu$4%#{emJ`5RcD&Y(O=ns6(lP~ht<25!5++cNP-R0xoWI{q z*ypR#VfBfs$q|hXD?Wg$B?XmWvOe5}_cSd$3Z=!{#heYI5!UW*N*_V)f;}5|`iCgT zn}dUKKP0iiQoi@Lwomr!YgoyswmsuN!k6^4rZ$^Ls&r&6H`zF`aB3c#oYxPI|E_{G z=s~^QgI(sCsaQCuEbQ3ppW!dSE0HrvQBzYxKRsjS6nF4CE%7fQ_y*brF0p4&$Hqk0 z-bWr~8w{9WsDv`qg=hZJJ=Ga_Ntwa9)vkHCIUMgJX<>h77spS%3duMqaz{-tQ+`mc zFq8;BZbn{gia5c&olvVUys@xl;kp7@w#PBq2_#KK^zE*i=>BoV@#!7CwTNqr2iHrHpLJZGASubPoE ziRAT^{OF!MB#USA0XclmhKWKvJm^&sei$iHQBm}DCQX8fzrViWe||l{RdDN#h#xBH z_UxSri(>Hm1*&l&_@6?mo;mNSnd8~TKtS(Ksqf3Slqw1rId}HQs|XZQp!5+gb!}2G za$*(4aad^yfNq#7@#IQ^Fc_Z~q%uGCRbDBZ*XY)1++Mrp?%|>9V*$LeaG;aJ6M06g zRtnX6&XbGL5O&Kpz!Zg!uCA`^KQi(f22s2#`2?~HHuMdz5HP>s(9HC(91}@4)2fyi zd^=i1=euM(Jl9$W(7$N{P{}qw6-0(M(H_q>6Q9r3-XrU+NhUjl4Tmy%A||!W_w`%> zU$|UMR`@bCdwcsM+w~5A8(9pvwxlFgF;mK^4hPgu4QUu7CT;^mV~`Rl9f3|xMDS|= zPz0TjQNiw}J+D^+Fr^-0(5~t;9Ee0Azw-v{dRB9W?O;apwg3BT@W7j?O=q*A-lz$ z9oL6#`fUYMhm5$%RhdMRZsrhoy7hv9`Yl@KxO^^ObXZ~5di@>ugPFqcOO62GEk^Y1 zdHHd`CVO%VXdO zH9c*QfCO<&r5Hib)Yyosho#;Aan(nmT()j|=Py__nU@Bo=1XHHb$euHWPHHI>j|md zx!fJY#ZQ0d3^=nRnKS5++!{|~&oC&Q^>yVT5?;4&CUc3=VuvsvSw5GhCZ(xqs04XN zP(T!l=d*ZCk2n$@wVV3|rI`{m8Eq(}vkCNm5rwG0qAWeI8*!=3w}LP#C0>Ke-_K_P z7Wjxf8(Ukdnvl%YXFPmlJP1-z{YHCbCLa*j_Y?eZ=c232XZ9zG$DB^koqG!YMT{0Q z0+Wm^fz=#6obU=fhfQ9kknuSaQAcv#!eheCU^@bls2M=#O;1hVVSQX}_58M{f96CX zJ=d!ErA+-BM=Od15?!HJ?qIg}Wwk-;6|i>64llax1jNL{I_r7tZERgSRkiyAJAxL%|kpw`QhhvKPTw%Ck&pn%d z|35MW(qBjI2xlaK5Qm;vf5?u;IiDb`O4md|+eK8; zMn*Txm7IOS!os4g6Pe(wp-dgj-be8-Uj4fq-Y_z{@CXK;zD(tH2j(TS3m52Gjb(w} zk;7~JJTZKvB3SN_w66aAc(UA?$HskcefHU06QUcx_KwEHIr^a&qJ{(Uw@Acziu-0t zavhe`S@YTYu6BzQzfUW`rw50zva)8svt+zZghVIw4hFfJXYISF%|3(|kGsEqlIItx zGl9^78Hor1h%b+f(U66nQ5ysV1TT*5gRtUlNHIvA=I%g(O%TP-$rFq5B!qzU&*zPy zVu8p51qBrhRqzIUf&Bk}=^_7G4Ca#HhiCS2HuJ4g&7uA{0Ge$CA_XQSBOzUY?f?~| zUSL8e@Dc!A#;=i~Vz7REi9C;_FRN^De{6CDTpU1w5!bU@0;5B(*kQl$fFfz&m*5N7 zBmldpGm$vJZvnd*-MSJ6U;o0S?MsupR4u zWd;chkXKgIp6_UAYe+NNYALBJp&JZn6#B`{8@jZ9M!*kNX`KjxW;8)y z!@{J^_9ZK0QXVrp3KTRDW1~RDNmEtzZ^y5i@^VfZuCJ6bRF>A3KU+K4L75(0eE;ja z5Cpyp$x~_(a1pV1Y&$1@mk*6?A%dRhg|dZ(g(JQ%1P1>JNrKd~s+yXW7Bw9(v%kiJ z-*sKhqxP>7ct2_;eS5mz+)%g4?*t-scI9^9s2v`7c$@`Re6RdZh;kv{@Q8_j2Cjn> z$^qkprJ>8@o_4ufw&fVq6Esy%F8~YmK2u({QW_er;<4!e7uF6MaO;cn^A|uw7tk0@ zO?|pOtt{;+<)VIS{9N~4_rAnCYrm@nE2ZlW?-20Dcx4*Gasq!I6il)!_$y*iV95Pj zivK_H+ZzKslQ)pMnoDc!^V!6U3CuDL#Mwf-v^$(ia{D^tmb59 zW`ftgC;*Ls+UDls;=?=6R$c~%?}deJYss};AhO$2dJvGBo7)kvy5!UOz$TvZ@C*ST z9WzCTg^df%6de_1vOGNvz1$mty33TEk#csEa|@JHU`J_YZZ?oWDs#>+3f3bJ+W+RW z|80vWYu2s-6{0uAZ%mPDzkv>JW#y68=ty5bO^b-%?a46+r2%PAsC;#KW!ycExt-O3 z=UG-(7Wn+r)ze%2*mQk;+3x-9qX{k`nwkM(0RVk2lSnw6_5`?l_tW3Zfb%+t_r+Kc zTn_2ztidF}A+y@B)oE#aqMKup%1>=I1FD#WFb6L%L6yZO;*y+NrtQt<vb6l3 zXEr@G^`YKvg2rB}hLwehg`U2Pofu5?t`CEBEkWY2Lc0bJ7q=*YEC1SW?fyulHg6T2 z-cFB>P^fu5Hetr-sTi3Vsql!1_(3I-09zjg808L*|t_^$#my`RXVxO1oyAk?)4flv&RA4 z@9$3kv%P(4A=pu+`}TJDC@nZGQ)rK2M8a)n4ESU4a54M+;)H;(0e1fE8PAc4^4$9a zC%01!8t$KGh&PFOwf*2o9~-z3L=U!Jm(uX?4iZpB*oSlQ5BQgZOI-Go)V1-z5Btxw zfvk|r@3A;V1L|e~c9deW0hB_g!v0;H;BIADYf=ElHzl!d*5k|u4K1J%s{nbme)fm@ zx17AB+53gPetxLjuw(;F$o4DNl5=| zh~*sU^qtSmYTw4*?Y5;w5IiqPa0$I{;;N&8F(DHlHRj8JtrlU~Fcl9S8zΞ5TI|+K2)TFamd!nuX`N6`jl={GVBXHimlD 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 ^Ky?ps{{q@Fcf$rOqlj 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 Sns0zdP&CzaRcn;3!~P4_7ellF literal 0 HcmV?d00001 diff --git a/.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2.png b/.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2.png new file mode 100644 index 0000000000000000000000000000000000000000..517c6fb00a939d16073b043cb365b997aa4027ee GIT binary patch literal 1157263 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

8vKm-q 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

gv{e4A6V8joE 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

Bw6qeNj` zg=Kqs>lqgnVC$lN5S1$-E`5gg0PXgDMkb3^tyev3A3#fo=HTY)#%7T{@u{kscXxmP ztk&&XL)i85?76aeMAP!Z4&2kwz%yP65Go+@CU*(%-*=q9;50~RnFkjk<oAqoT2h;Dl&D6VeN9D+RG;QF}$6KOMmnr@ggCTf zy~QZdRaB88u7u$ecfAAT(b*dUx_{!hTX{m#sfvsuTz3t@M661DN_daUgM2@R$o#Zf z8~we!Sd^5QeEAhQh~snu3;49OT|vH8R;%CbR2ap?0$QW; zQ`%kd?k)AsI*9X05#7qZR}-0_G+X9_I`)Mvx0)9S7`4G&r7mcF*(L+R-ym11xQ8Ypd(o zB+>JuH$2RnIZRtjJdHE-l^zJ^#l(thYnzkn&f~N#H5=-k4xThvhicK%K$o#x>+V{_A*l?78uvqK1gZA%4FquxOyDG#mO7oFfM3+c43ZEYj~CMn*=E zh$uO$t2;V^tvXZn{@6JtDyI(z1}0PrvV%1ks>f<;T|s;Y1(`VLPMhO$2{H8M8dEFF ztM{x;!i>mqg@i@+95b!(s=mJlOFIn6`;IU%YQAzlE1GZlnx7vZ=14W?4{r4FzP_>Z zpXYJoM*8~qc-(GNQ;WTQ3$u@;YO9toeA7d3-UJuBK}bhu32Ui|QB31;{k!wO%0`v} zmyaT>NaJ8+beNr~6ZE}nZlXhe_q3-a-Jx)1l`%0iIDndtZtZ7{rID(Ujl8^*aAs-g z0VKu6Y1i2AJmj+ArZY4#vC+@`5oFl+fXxK?&Bt!~VsMkPvm1KyV{^EzLjoW>Gc$G@ zmI&%Tn#1;Z8!i2}k}@JLi-#M1;0&qBPARgEd_utZX1Yk{EpQujEKz8xGti4RUmvGE z%Tg|6-qG0=a!EhdX!S1^fHwshCqEL{zGP)eba%{=JzJwzaJK^;H_<)}ZRqRljS0`5 zQ>w}DCp@|@FCU>&pz00@Wf1f>><1oH4Gs38(W_h|pa9T^o*5775t|d&Iz5$+%eG9p z-rlN#ttJ4`1?1mq#)Rukwx>65BZ2%11ABe_7o-O_xST5s3f}HC7&*(Ni)iL!%MjHm zOy>EP#FY=sA}??9j~HRr8#d!Wp+MXQEzv$EI`y!o=&7iTni^+WuKYC4M-vnxGD@s{ zt6H#%K>3p%ill|DWSM8JfSMEM{wf3q{P9s>zN#q7HtLCE7QK!8G+F<#+3!tG{FsX< zILUnOASpFGYprQ+PP5_Fzd$=Uu?h-;z`{M%0AHc@7671Q>C_BnfL41d10lD6nL^g?P-P8WY=5S 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

Bym>`w4_AjprT3-P4HrpK9Xq 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%0AbGK1LR|vRQ7TH-OhR;&4MoMT~3D2C)2Ug!w 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%0Aj0qC! z8#1MJcgHf%WaLoBUKPbPoQ++?`KV?m8}(d@6ZvThr2R5mF>0$@bMN$j-C3z?tnU|j z#iV7{pOv9$_ziJ}Oa#)oHok_M+v5(hKc^Ah`1ei5ou6n=IM!Im>*=9hq{bJg}}4&Q#nS)%pD3o^^Z6;YU*dnQfpSD^42n*Fg&imVj;(j~6EymcU0e z471X{#~x?FGaQ>uU@Lo%)Nya!zkRj+e2d!j4|VWZ2bG4(jsZK7Q~H&d5A_<>lFiX# zY&9w$!3xkvgFJND4wNi(_PPXB^>?ZTu@xjd9#=(*m07lH(bG>kKdP{N)ZY@n?Zg=G zE0qPlYb2=ja*KU^w~KSXGBY!i;rsf=pfesH5HY`U+3ZED zW%3B30%5}m4)6NT`PQIJGDoFm!?}K0j+BfJJ@G}%D>%0w;|Y*M`_nBCHor_Z!Yh)& z>fqO7HI#T*qiLJlhB1?PSL`O3MiN5{Mmg+Hip7oqn;EGT7Bp@zU6-G!UE|bu)L46a zz@jdld#6shbYXKeg8*VGu2_Xk2DwZ)87yR`b45qfg9<*tTF%ys0mlZ58GA|4B>O_B z);WhKHQbF7i)JmV*Xw$N1^oTv+znVckjvorUh`+4UtIMR(W+KJL!4d}&s6x!&8`57 zsyirZdRn=yTwANwlGD#FqwN|vV1SK|_shlT=_x}A>o&c!#%KQG79SwPvY0ISrp0!h zpkrxuVYO0QW7XsgZCI|;5)z-y!@!n&-;V;V9+~~z=8A+hi4}Vpp3L(5!8|XC*9-D` zynMr(U)?VT4VEuQH|T2$tA%#1xe^YJhUCWKp{DI@#dx-eI*5-iRu>*}Q+auQaG1gP zB45E+$)pRP*8H2<$cB-&9?O zU0NiR8yg!TU@=J&v0TL-9@1PP41ccQxVdLVSOc{X@L|Zr7Nz@YX)$8+i&bS0pVPkH zDupd*4S%uig>F9$Q@3z3ES%Rkub;9XNS1pWFZZXw-x?rc3(O3be01A{M4@a-voHGP znym)oGrL5a-@E#TWm`R52fae4RSVmY@JPnRd2I%v45btM4g!M5{r0E!6C_1LPa|Gn z*xG}48o`qh^TYZ+F{MVWqZugleV&{;K|CKiy?Xuq4c`Y8Q=6^o9FhzbUQ-4R^!4=(@WhB9w<|(wdxtEN{$~nUc(aT# zGcZO^m8juV`YHzDkLCG2Gohm^>nsTA_!=H1*lidOrxHe-`5l%E_}_Gd_; zFaU^9sJ>781@vFObL*PnEp6sK10c)ZJm9Qz&*CmbNm)4_%uJ7#TcBDs8=NkTqq2lU zQ7OMehAq*S;jy)Ib@R;4fdK}}yp*HIojwpOqmWBVN=bAbd$++okAU$egX8=Y7~lo( zp^fwdtFUB+y38n~=Mf@lDvh%5@`YnF?5-OpI`v=l41Jc%wOLJPWpNQCFTwquDy=Y8 z$K72gl>FX5Zc4L^nn^UJwz^LqF*{O{iTfdDB*GL10&X4(5(0F&5KxLMye&`&*rvwD z5zP4V%15C$!9hW--~8F17>blAf_)_7Y)`%M%=7ShS_G2x?@ETE2==_^>-ZRG8m%VG z4esl>+|QyU>>kwP((3S9@7YN9&yVMl%}~WNMTcg;S*3<9Km~}jdE@NKwqxHx@Y-%+ zN@<2QQpM5*aFUSZA{tY`T?NU0g7dGci z^Br8b%z}K3gE@GfOw#G_P8v|S z0CNrePxX=SQ_`;b{dILxAs33iL}j%_5JW#KM#w^9yY8QU; z`;H@1DHg15ZwIJ>4&WDH4f+sHU4b*1Y8riOtgw4-y`8! z$>TF~rv3Q{2?@btI_fd5YHQ%-u9pBriarMkLw(9f4tQ@n6`3|l&uu-2T#@}57)^urTq0kyTWm@D5*fdcV=ZY z@q7K^sPvimIhrrvd!D^r*N~X(T0Sse;#nQ*cx(C!npiet<40k*>EUsUlf`sI1_yth zQw30@ot?q-O#u6l3$U32Hku#p1;&Vx-j;;H`;JCLwOJbLXnJ8J{4w23<8~2|Fr6;J zA`)Oh!b8GABkjS(JJSJrj4KP3sRW0R20;-|OU+*CG!_FKFFO>k@Y^fF_#c|hC$NDRDff`R zNsNqgR(yEe@Qmx*v>L$o+t(lUk1C?eyCa=uOq3NUFr0t4BbYNO*58lERo(l$V@{r# z-&lECXBko`i&rXG2WIs$xDVb!3+!1&Po#qJzJ-5HMb)ta?%sX@0YXB4diqDgg4tBM zCV_ld9606b4OO64;y5b!GXXAp2`|h8Sxf_oC2~{&0Ra#R5peV!Dn1^4|AEvP5sRz3 z)_v8v#0ez+0_m2QmsgHF7DD*y&sD&7Hf(ZuV(DW z4{>$&pRXR@yI=0E4Omd$U!6o+RT=b-2>8RcTwmaE*j5;0e~R?7vN9^=x#vbgih_Jg zbKk*IsD=Mh_=h>(hHEZcYU#VP4rT-i1x=%)Sm36jo26&8!P*-#5fB;T4#u9_zwU0` zR5DRp2}Ja#G8<06;W$uhtv{XOGL>3$U|93;d2KP`Tk?5Yjnw0#NnmstXjuHBBTh)5 zS91Kk^BehIW$mJxL)uo=Rkc0k)o|j#{D(_aDw;zKMYTIP)OK=X#f|6^nxlhY`_F0` zP0~wXAQO(sySVbg+Il7u(@t)W`pFj(QM*CE;bK5(UA9|oE{SXYbvy&x1i683uFrLt z5KsGu)<&NfC1>Z_^w#wI7dV|r3mIce%dGM#1;M8JPL=h7cLIWfp%@syOZd+sllMVq z*(r>PEEbGRs@?1gQig1-7HVc%hlDV(L^1D_Lk7sHsWmWZD5(!_$FtF>l!eH$czpaE zXXtdasvHY%4N8-1K*D6D{$15v+f!b{>t-Py8H81kZ%U4Qq*gp)Hs5Vehl8R0E z%G3js34!K&H=-!m&Ej!{car7HUf68rVbHECtu5Bp^q>MD;ngWI zt^B$Av63E9Up7Z2=x-J@W3G51Xj&I)JTMR*t0!5QnI~YkR$SPG_QUltMdv!+J}fOQ zRZ{S3m+wy&dptkhVKEzr;j^kYOKtAp=sncZp43H0j3ny(CempIPJx}maFx6rgoLpE zpL+FQ{#8Yqz$Jx_^hWRrkeAcgSUX4E$_%H@&L*B20%XS#MOpMR1#{%01T-Lh$B##%H} zu=4YDM1q6KXkG!gMzE)s=Z{cMbo%3?Ez;+u?kxQWYBc-DZxhlul~$q=xWV+&ETbi= zwUXyazzN~L^}NCE30r%iS?w1PQ#^k%SN!&!;AT5`qZbx~@-G~nsyz^UHySo93Z%tS z&&(SrC{I3u9q4E&sLyoLyR>MSYg-uK$(Ou*k9oYD?v4kucL)wS+<63-#y7T!=>^|D z?W2h!q*pETwmI>0hju8;+?*^@aC~yUJRIJ`N|IT6WK7b`L@q;DG*NOr|1CILX0;!5 zLOOLXWMN`Or)^YgJR-~3(%IC9wg+$pHuO9*`&_?go=#oT{bgH_DS3O0U2}6V;|)}Y z>oz*guHQzRK;`cSW`uk4xo1kuyyNPs>SbEL$RsFE3~Ef!P%-O)s;Bwg_$zbHTzNTJ zFFe0{@y~i0!0We4K3AN(wyjMY#tCo!t7bacGqBDiT(%NFUn@34HOoL`Z*}coFH~4r zQK3E%^h&?WjGl=-py+>m^t4%N^uOHz>9mxT8HR0pbc)o9^RAiE1`<|)>)Lp>C~t~JUhrP;R=G-B z>)6Xkp&>A;i-je{4&LnIy}xErrHVlmY$n#C+EXNAr3Hz)>LVj`$HyndUZoO&j@={r zK&sN{wh<86#BB@~qQ*m;q2*T>7r@Hc9y~G@Ty}%LydjL`An*tjP>zoW&xxo4(Onm3 zyN>Nf;QE>ynh=6c%jK|Lvif6{)#ACLx7;>8u3ItM}*n33My--h65 zJ|Ht3dHMA|+#CxUbghMT{VHqRz&|Hzzzx?zLPZTCh{WR#8EVvSX#@yhul3Zd&(_a^H^pppv0YY2CQqkET&bISwjx(NK_u4L z%INCI`TN8b@Js@sGZc5oht8=um9Ef^A7WR>+A~QK27%|wn>TM*V%8`;0N>v!9NMGX z0*t@Fo?K|!x4*iMc4S{Qir?2ynu|jO+)0>tcuEzYy^mZRo$c+so@qoAAN4UPcr`!H?d596Ti*cqD9`>fE$%ZY zI#N=l=2oTVQhot}tcHfWz|wmypEo4?2V%hBjnC{4$#8BJu*T-#VnIpeu+v}n3kEgN z!xn~~AAfMfv2P$EkemAU2!>8gVN;&;MRm12T=y7@7LkgW-TKxz^hR_AB7o05r4;pk zrx=cpRK*FXh=NOSeOT8~)@xAca5q3-_+ZmETr^X`zvBH=X_BS|AR1ghdKDlB_D=}X z!O@ii7eSyi<`tc|01zl+UAJcu`1Fr$_p*}?w>u6uy_T0WSqU_2#z#^T~X$ zI3mHl$Myb|cRh;NTdCxUCK-ZmndfqyY-_Nrgh|I@ay`9;K>@(Mo|RRD`;~aCwm!DM zJ{ppsuCDBhx7Sp{S3`Zq?co%iO8p*=D~wxYr%cn#GCEvXwCLg!?uZb@=AzE-?#l!_ zFwXSYzCAMl+gec`vjSGlzOT=3$z;-V_OMTZaC*{-LLxdEStf%^2}lhI1q1Q8Ty@WL zfP%V?)IZ12@IP%lp4XxiY^DJWupqz@7OZOk$ zE*RdUH;E~%HDdB4X?vIQ`?PU{e~Zd2oa?3e35JpyY&M+Ehl$zQ8|~kGyOR<A zBARQp&&~c@Ffi}kY)_Vf3DE(&X@&LEJ#H6+7aaw~m!{Fs+U?=wQq2ba)zuCT-s_{K zjf;VNi#qFddx3@8Ba%)h=hJ?#rwU3~)Z5v7#JkS^81n65osV}BJm0H;?7GzB`mnO9 zO6ZI-W^Zm`!NBHtrRk=<tFI5>GCG&v|-8_ie8HU4QvAhKCngBU)+4}>$vq}u| zrGa7hm!0;yz}|98JbyaUTcuJd1Qm7pic0x>trwKEzUnz1ckRxv;h9nom$TG>$+xfX zmlo9^d*@+Swr>T*EftzJEAM`3w~(a68E4@cVLX zO!kHS`HL%3GSKIs?s%pisl1#8I|I&TJ$JFL{ zqMo|r<>|F)c0TX4|H$Tnem({~J}eN)f=rwEeFGY~xNa@TwW%J!{L_%@mMQzIvf@pG z&l{kC{()(|)NC}i%D+y4aR~h=1uP;NTbsv+@|B7+#vobNIFzyZn++IyFNZD@cTI;g z5N+t`bGkrw5`U4$_NAF!FRfq_qd?nif0gEoH193<2piaa^HGfiL0}KG|g=h;1>9ra$k&*w@%NYzakh+{KmaVLA zXkCnC@Xh26lM~}w%~dskGDfHQW!1;I@&Y=CHXa=vy0)SLY&0-PG!+xWKNqQAVe2$; zN&a<^l1}GZDz#9t3u1hqqg$Ye1b*)dDhjVWTwN`fs@#TVb*jr(_^-UzI^FD0+2jlj z4S_fdD1k!xv_S~f*w|-}a4u&N9~t5AP3+DFcE*@ba#vc6UOIh zN!zD+R?DMu+G}u~AD+*dA)=7o{}yq5xGo(nR#`uK^CGpP{e(+>PT%GEeEW1`z0?%k z)k5sUALM)Oi~A1lC)RA=@DXSZKglcF>75pZVvGVtN2hsA<|#7bn((DZ%1;x2P7TQC z+v-JTF;*~Tw1^y6#RVjlwW*iJwy*z4;l6&q!~RZ&qrvP29cmJo)FDlKK2IlFXMD82;3t1~+$U*kU8x6^EjQsp8?wcHx0tIrq4ke8H{)GTxr#Zmv3Nu==9(KklF zJH|Wz2sAUm;*nOpSv6)r55)6n&-Q_V3i^dY>Jbv8-s@`m?I$8ne_w-I?2_x~j7*5<0R#tEtviupP)OKVBo_khfY_ zy4a54yU+vz0~cJC1Oq7$;=-H3M9X@0y4D54s6hE0^I<%ahK{XsXi;3?a#a7TaKq{g zefzz{EpDM~Q5bxi`sboQ%nae*TGRc4o|V*A*z=g4@(qhbd+rDOW#0}OHrU#upa9Q1 z#HSb1IJbuWr?p!nGaQ_QiHhc#lCdQ^dBEVelcY30j{+IB3_G%vR{Rj2?ALFn8yx_kn%>O?&(EeLQgtW49p5MP; z>P`9V{xv72>?6&YU1FY`%?$MWr^|R2##LR{8 zm#&no{~|TzmZRe0Kze<8QWBwz1RD#Jp1W|vmw&^e9su}M5{HluqY+cXQl0W9h7kO3n95R+NutC;^Y2KoQ|P59>(|NTDqlL$3X0n)cn z5dh7`M#iiE_3Z+EUeVugx7z?l?)dNNqy*{}ia%jt!#VQANs=-uDpdagotp`%shNq1 zd6=1rNjW>l`h;X;bOd50{=*UzYUOsijSVfT|K}ZW(~y@>l_WC=5N_}XSnBZ?70M*- zfBjaGo+yTfOZ|^k_o!gxtaZVE`4N0~nP2{W%t62KmLDnqN51{3{s7uvK9(X5me@c6 z=0C5M4GSAm6!-rRip{TVoTpes{tt>BqDTh9F=GKA>xv;T0HLOa#f0@AEIf+vfv`x<@g`{ALbmR8~ac3l4wzOdQ?U;cf-goOU@0_8!x10y5x{|11O`>;*{aY0_}p`WVP|I-t*K=k4&YjvQy1yS^bbxuI!0 zaG0^NOadg&x{ZN>KG1-ky~4q55p}MF&1e z>l?)?Wu6-n&?P+!HuwQVmTQ35kZA1!(Nn-qisU80u%nGN@I~cCnmHmUXp9$+$Mg1d zA$$$YPs|Dnqis6>`dvTapn!NkYE;-WM(H;?pIE()`RuFHKe-VxGOp2+H`#4KIyd}1 zB+p7rFyn~pR=Z7W6Kq!plNCM*BGh%MTni;I34`{M=UNWP)EVLqWmNt@9z_Olav%i_!x?sCwzfEoXkEW>3rE2 z)%tW#o0E)Gl_4ri#JVwR*?-X2c2yXMxFRaJk-YV!2fpw3^aDH~Eua5C;e6 zsQ+{x#HIi@B@n0pcYhxMM$z1EkFGrfapG}wr95-%!f-%zF4t^m*PU9>^;bYp2gKE_ zpQM(3+P}7`GxsM=nmuOf8Z*B0Q5G?IeTE$j_l*; z;u0DjPPr*d(Q63CrCsGY@><=ox#Dq*jI8|Q3%L?AK%-zL6M(vRnWQk9&f60*2@u!| zh%OPq^SFo!e&g+yhZ}+Kf+SpARJrnmD!|rd?)S9;&Sx(RUEQy=C0`^oDm~{)=GuZs zixtH|3dgXaO1V~XdZKEJdrj=*CacA?Wa=jvEZpUu&dz2qI$vi2%1*%?|5S;%^oZ?+ zOu;}{G+a;flh z13hp%V9f-K1kW_C2&00nLZukZFENL|H?(-(Z6Bo&3I?n*es2Q?NK#2mQ-A)5pQ8m` z09%75C+f$Zm4E<0e2xkmu&Z8=tg~67lCOtWSC8liBOitOFHwN4S*y$s%#R9=@@;wh zCkt339vmE4kS(-0BqY@32M3Fx*D5M211pLa_p67?qhg-Fe2ngqUl$LLLR{vX2F-}* zR<8#HFzuU`L`OxfcRtDhdnINz#*UHs8V{A2JgKN|VbNYwnShTjAaP)p64Xay3u$^uT~QpmENtC&8vc!Y_;x=HW7Q!o$^_ z1J!!mD=QtG_0)oG#l@;lVUnvsS9fHHi2r9HrzpjOG!Pqv4l05@Qf~SWR5$3&^N)eME zvt@vIaIPFBT1afHVhPPi^{oo?DAq*&9`I&hN~(u?>-+t8WX8{y4$^(Y{jE?8)zGk; zox`x(+gkuQDHqBMd;EQK0nb|MY_(=8eA{HUAtL!PPQ0y_tL|j6vSv6C<(6IfZoCUB zg{=W7#Xba{VvyI{Z&fv=Jop8p0NmWG+3V9H4@g`(zcch{Nak2uY<3cSY>F*g_U~*U zkxGvz>t%enE9+?G@IG|S7Lg)Z@!ND6+?J9akxp(i0@v213nLh`1sG&Lc6qR_*zZ!k zDG}@9jyQan08&NBz+OxBO9xN=s_>}??5?@-=TSV94P=G%1}MpghF@N<4{~v&yU9pu zuJ0fPL3Uj0kp{A%?!y3(PJtwfEDn%bi$euDU+8a^^ar|ss?t~a0AGKSLd z{1OJ;QTmbm;%m^z>}>y7aUU(R;Nm*Fx^4CHkb4i#r&NhXSa?{?FUawlgBfne-5@CbZ{KJqmDKEdkwN@P z28&zBwc+-*ageTTtzvd1kcR9|_R%755<`G!tpjMwhKFe1tU&%25(SZbl(7S#eZ|3ngF<-o6ZoYWL#I`s za=h9=Sx19)t0j53nJWIkEe+uH)FaSL`hNe8&+A%Yc7cxCK3nAO-pM9Z?^}an;%`4c z)634rhCcY!yy5Sfmyj?~l`ID6ZB3z6p%4xcPZ2-Qh3NCKu~`Fbb+*FZEm;-Q{Q@T*yDFchL6aGa34)%!eB9vo$4wAu zJy*WbOPp|ac?nqisB1a~M%F2kk2@pjv^tqY&%&yOW-#j_3)OaS-yj(Zf>iG>-T2Es zKjPAi^vpP2jz1l}>S-TqAM1nGSOq}wY(?U7R~OIfa0*aAfOcFe6>^;6d<<~9U`>$; zB~*2T~e#7KVD4n~`y1QKWYkWL?Z)$3Ux6 zW(ii)>l-L^T8%;7o2!{`?HlyhI)#(_@jrR4?yY_65n29Ta0x^RLCZg+J{AB*%214) z+^z}~Cd7K(+rn^)186`C@X6&ESKGgKrb#9PU%og{M;LEu?YhQgIoy?5l+;h#|?(Qv)p#q zjGhf42l5I>bxzd)iU@<-^S0vfS07A%J)pDoQ*f}b)|M(ywB0;5gwlxHnOGUo>QHKD z!QHmcUf{Fa-tqjFum9wOo%KpBD=WY_I7vYSdCh5!pPRC=@szpJ!_|wxcErU_Q^9U* zY%D#bswo>d1cL33SNl_7Ow2vIU^3YD2k)d@d7|@AXQiv1XT$f>P#X;VMIQz*gab4W zO=E6uu5s1DOc?|ugg)7~Z{NtsM7V2h(B94g$qhKFY&PQpsqj+NG&HE9xlK*nBMdjk zi^3#iDml41dCrHkg|27of7`EnYk=U0{wbBhdklnPRzAd;Xk$Ik-mNF`{C<3KkRfHB zkaF%o{x~r9+2-M#@pjJ#$w>D1&njeYsh?RBvFj9&5%}Eq3pNgCE$&+bN`Si=P36QxLJDp_ z80CBZJj-ZtZKEIi0U6h?X*BpG=yQ?W z#&7*I`r`wTGfdXqyVrj?v02y?nvlw6mCON*c4h(E?- zX*CeqI;U-EjtlmLiL^`eWGQ2*(B^F^%qJ~3!_-_Y@U-ykzQ>}j5mka%BoZ=GLTNyq zU0k_sJ%|$tM-&n>PO`_E;voPM&u9ReNA^Po8kOSgzyQ-HqR#GHuhy?zSZe9z8T3`0 zmTUG#gAI+mDF8-o3Vc{}a{%&UGd;b+In!*BNVkXUBM_x`KDR}!(P-%?sR$w%ky^}_ z5&8$yn(Dd)y0VsAxb4>K8D7F9EgQqaB&)Hc`o3P8fYn1dHtTYeOJqg{!8C*vYD(S7 z*Jpn?wCUDI*{q18il-Q46eM|Ftcs`_W~r$9@6cFCBzg&iPyLLqv3~<8KO>YO5%e1X zS!@rbMJ}SGc=Z&;^#!vi5HWUleCL1^z2B0+_<*&0um^~l$zq)s(2T|vH=oElJRdfI z5BKcWC_M_YRAGf-W20SVWs~zU3)@rTJ=hxA@1E)bBaRtuOk8U~c8k0#bTdAPAO6PA zpZL}uU8qdl|9pPR;ugn`HJQmN*_i22ym~FY#oZ#kW6kzrTT05^U-Rnu$wNOwj-7<@5K`ldlzob`j_!A%V_<-xNr+^)UpmoCoh~^bSwD4F9M#P&u{Jm5%6kF zZjQmSE*zIr+~M8YB)ipp7{C_5T`@bfSOUa392?+YS31`Xz+Ryn=v7OO4@oa?1cn;T zw_qP>(Yb{Bw_?a-&^2O(pu6c~sk9(HKsMeJ85tSy3xRoUZ3(Yzc)2S#zvA||WxlSW zR1A&Rh%~?4=;rX~7Yu#Cx1!l+ZYSN&|8zUP0m}1_%7Qcn+1lL96Ompew(wa>8;B_S ztR=kXItD1jsU08U=4Vy^dc*CWfJqHs01hG#+esV93tDPb3pXR?#=*jTWU}$tm=~zD zTbp+9ocdIup%Co;xma(4ZC*90G@VtJC@d%n4HG+DpGKmDrY2ysy;nK-lyZWBitTI! z<-|Atq}B?Uz>M7Cl-*gM`(oVo_qL>SK4&cq!*i{NRJF}r1>{zGVSjc>u zt5fI_K8PIFambP1)32T0M!rwEyfj1v8~&r6eDTrGPkd(U?+(c((LjnCGC3H8B;@B> zE!EDw)bI^lDamAj!6}>R?1$X>RYJi$sk9PDMzx2a;*!cT{Au&!E>jA7 z7v>6WfcdR&q?Y<~r4ghdQCmcYY@vK^z6?Gb4?>>wlV>u`8s>n&>jvAXv97I7|BDHe zp>AMlthQKhrT_>k63{xUHTOPg(@D_g951b|sC0NEDUW|~bH0}93v*j)vo z0V=P8)zZ?+&XZ~@WU^Y(z#s5|SD?t9-vhz!9}mskAn4%kx@T zSa^7NSdIe2mIOyC&gU6B9`{S3C$)i|o|#d4qEDUu{c2gQ^T+d77B(x*QPi8jI>xjy z05V{Y3N0QtvL{`Xl9IBm{2A}$`(#!6y*mpD!Zifm5bZFKtvPMnvmO}<$~^)1tWCH% zfp;I;bX&)i#OY+H)pH6sUaNDf!|A7izH@$o`*60QYpw^P>D3v9vfh3y<&wvx<)6(b zVM)*fa)S{`ETuwJG|=m_+05yWcS3*H z7UP=mp2A#nb9h|NLK1Y!)tKtr6ZxY|On<;cX$A6~tDEb6i}%wYeVZv+{N5gz)qm7i z(3yz8qC3|@bAtWx*1isJp>C!`iJFmj?QEkL$O2S~m4~+kQ9~0)mphgVa})rQ0S=I# zFZM}j8vY&K{+h2S;_(W~FXnDopbY#RAFksc5a6^s)-f@NB4+?R#cfejT`#}DSN!nE z$<5PGAncStq0bupQ1-K{R;%8WQT@AdVa0b~=b=_-ZRM((I5sfFlNbO%9*=9A`8~*V zo5>2KEcQl?wOln_jzc@;UHNt5rt0ymZ{vq07ad+qZJ+DysM=D&3 zl~133^|MIUOpS(1$%m{Rk?<5;K5txdPs0gj6G;LN+aIoX%-astwFnxktz~C{G zfSb#FlA^l=@SX=V7N@HnEn!16sfJ_Tz$xp9K=&k8U@}@9f@w@=!EbgSg?{tLsS2$YlIC#&b*88wNo1 zdldTl?!9QlbBH+mAmiCDx3fVzwV){57dJr2x)erQ)Y#qJF80fkhJ80E6$jneJ!KU9 zNPM||=K1xGOLlJ|AfQr%5g&pTnDpS&XDynq{{!Ag(W51RLT1JAI9eGevOKYbKKolX*F@XJQi5cx6t;K#D!sU znn1_T)St3s!biX7i|l?^`jI5CS%z%?!*@^02a=tlO4Pc#>7a0ljYxWz;GsjDB{*XSo zBKI!kyn zlBbOPesO&owy*#o>JRA9L|R$d4(ii4jAay_Mne#9R*ZJVR8`WETT1-&X=No!E>oPu zHYnKt7~}j-GT6=hC%q?{L;M>l;msH#K>BT;0?f+=Y=cc^z>0?OAC;qVNc4 z*eKdMPNRRbjFYr@nc4ub2)Lh%6LBTFNVwGSpe}&NH%Sb==1htE7azY-iE;*NY>irH z>=%e;b2%GSS2;xQP0G!u=iu(Pkxeq`r3S!xRe{WOuZhwl5q&F*V=~TqgN69%pytSBenA|hxXb2sB?<>>UC}Y(1F_R(?XT;sX zRjT81YEpHqaC>`|fIMYtzP+vjY1I? zR#wdlljt*hz+|lVxkj0q>1TImXEetg=@ywj~-b_M5x@{$Ut<8Fdl)M%Q+2T_~+gACH6t$|{hhph+8*B2*|kK$uX zaxZks-S1ua`gk`SoEBxU5u?T2XMy1%<(8@jgOV0mJnwCLFhThttv`S6Pc<^Uu3n7Y z-m|S}R8=PTk>s_*OmV8U6dt#s#@m1NSp8fqW_}F9&lh0E(21|S+I&&cceFJ--QRBs zF}~j;xq((<$&{h~!EJyCOq|YFV(bfB2@ekk(Ud@6N_g_%4{O~5rxc#+yLUBV!ttJ~ zNuC+(P#Vd^zn8)3lIzIoljxTIuI0nbJ*4chmwZdnCDysllLpj&=^TGFb$@w zp&^sgfZzEC0QPl-Ll>$vY7qt0L2E)1Wjvc_r#Djj9@akN zfKqTA-{qd5do;t^)dutU{ScmphFXW+t(l6Rg){+g&%#n>H?8x(ShP`^kKqorQ8aM= ztLW@>fVQi^udFBJym%Em7Zs*3{ZTlr@0L6D62t9a;lJ`V)38M&nffWAqoXq*)^W|* z0Bw`P!v3%iC!6Dp!U99DUKW>DV%41GEbd8#aFHi-yR^e>aAh30poVL`@e_>a+g`z0 za#ST3^H2BG^Nk;SB#zu`(f6a5d_%j$t)C)9ZuAG2QR8B#YJROBR?`f(C+2Eoq-5yA zCQqi{XG_()9^4hiDOD}tVRj%Ft9e#P`IX7dXVJI#DG4*Ba_L8ZM}C}FL7UmuL$Hn; zlws8T!SP|?ZG1AFhvyZHAs%f%dB!PUB1du1Y}jjcwvP^wL*cwY!(ItV>wTTXc`!=@ zd=LaLQ&pSAm6bv<0YBj3h;LK{oVXG9+J%MZNqB&O_7u>Ro(BNuC5Hn*D#gVkL2AuG zPme-kT(!$x{Byo6fx{jqC+K7WEnzUFDxjl3m`YKt$WyGpNY<#;bh`9Y!h&V>VcVZ| ziX<*J$*KMcLOy=hcw~F}n0+9&ogyQZR7O98VB_~{zhQiqOhBah>yK?!{Bg?Wa+9aAh9$3VgsZ9iEh z@1eFiA3?x}|L;51jrS?KYdbq3xsci`lY_;W-1ydskl?mU<2f{Sd_EH4qZ9kooL-FP z!%-$jAxg}9-c+j(t$HszNcxBMwR7XmwXBo<#=rr1p)K#to9y04I5UUObD5-a3_INb){3NQ<(;y?22SbuqXH#&}EA0=6sf=U*58kR?^m`lzExyv-$)g`;wx z-i{*}oJZ0|ZK*9}GOCn!-X8zfsCC%P8P0)&OInN1Xraa>>|0=^2za?2&W{7*!?0+g zmj5vTX+5a62P+qg44&d1r4ftZ`MK6kPrUkL!_2lfT6hk z*ax>F-kGLO9pKsESqY3_vkRR zP>rBB2l4q}^=L^?pCftDBGp>n>GwkFDX;CC*@4cqCHy-Z;|FD5kuQ33o;0JUUMnHL z4ooS6rnPMwHs1o9YiqyXmu$(e0oq4FTt=h|9VHXcCjIJ&oI*+m6weuCzPR-v_ku+p;6}Ex!^oshK963At2{2!c zdO27rB@)P`e71kQb+09^cb;poySzHLRenG|Y++$hS$SSpR}Vn|6NtWM+kYJx2m|2R zV+VE-Y0IGurYT-Uz76!rlE~y_;?J+GEtKD|I-1g&!dmff63vYW%GTivUpBjFLtQE? zi!G9^aL+4h8Q<7>(1(a*E^ZRk*^hfq`>wzGuGV0jQq4xgdCi9me!|pU5L_|_?`v*(Zt(=Y>5EfswUa56}C8}&+UQ{=90YZr` z15XmCxHv}gG+c&#P~bs4I2sP;*yZdPdfnxvSb8-|Y_YyyaKC@!a$*KG%eFhwY84h5 zL%Be(jy>6%4o!GN$yp0^5MPCVO!)(L%NCLhtCzmjklS?X@`${pu+f2xn?Q z&NZ6ExXf4Dn}f5i1m(8n*41DrUoUY?K76Ku=EH-6?eORd&kL2(2oYCzW0~=WE_jspy(OlZ72LQja!;r(UZ^9uf5fU3+TS+JChUQkA+S2{bi}oPAFYI4dh2MCU8}-EW9TTv> z9s7b$@Ml~TDLy$Fu2sWpeO~*5KOU-fjE*X}D1n)&5NN$u*IHy<5swBl9<#TV8^7P# z-bPzB>PIYj#2#y{UwVP70}z`7LL)XQa5=~Hy@Z@$@@`^JUw$~pnY zVc^7T@0&$=5AgISo67M+`kHmFvNI)uK-DJZcFmE?Z9Q&FvxfGdPDq>lR*PXTqi&m9 z?7b(5;^O(W#18v76K>3Qv|WR3sAU2E{uNbK(qOHVR$83FCern%+WFIzS6WsIEj2WL zKB)Yk;S3r>6M`MI;U?H}=U zV%O^h&wD_GZ$q}0An|T3fS@_ssp!DlBiO{zXOusRuqsV}R=1U%CiTr&PmgJ9KN;MH z{r&PphqK@`+32{*pBaFSJ~L9J#M?289j=2*Eq6b>enRG(Zn^+Jf8&n?5$)h$uid0B z(XRL{cf%A*1i^is7d+Y|6ffk(qE9~j@i1jm*zEohH_>qK0_oHOYqv6|76@R~OUvd0 zzefs#_pxM4lN&(VY;SZsan^Ypfa}m3%F-AT5?nE`{pHK&j$nc+s2Sf6o=`vQhr|gP z8Rk5iQRD3YRIBcQpA2}02G{>;V3xhd6KPL1*>g4A<>zCTSk5GjO`^X2>?G#V6}aBY zJXBFjsy``*osE7HVsKowF-VQ=@}s4sqVu&VTtCOs$~Qm2-70_(>>p@Q9|%nQvxJ+> zUd;YdecJy3b*vXpH79l732XDSdN^@xn0mddKP$|o-;-Qx$jZv*Ftjq(gM*7z$tL&m zvW~F?5VW$iCp+V)czoj8$OXd~$FD~7j)HtqiIcco?5Be8bnuD+-JK58DtdWLxVYg^m zAC7_`{xnt@+_~EWeFnLVjm`Jj&v@OvEiNs$0qY9l;*h6pYWzyA!ZE2N{gK zppyTwKmD@`$oZ-jHcs`Wchtm5MPIV7YlGV*=vH(~;H1|>%)XHtDdRP1O^ za(S+1i;@l0treY&tqZy0v4zpJ^}M_MpT7*A072!(=~HI4MusP+ zQA!zo@RtlZmll)8Iv+@FYj8QRn~uF%sPF&@ORqIt0wNfjM7O(!vypJ= zai*)S+&g3F;&?4#`MlbLRHeYD?BheDIr?bg40fgf)fVz&(d!%_v}G9>cY$07_OX;v zj*1G$9mVsPH(%NBx8^Grz6CGKPR-7A<*#Hm+tz+wTzXXrmxKao##kXvle5jIPVk^R zK1ME9K*FUdaH6~%c0meY=|}^>*j$j56vG2V_p9MAEsE*89pH>4DT~?^yCQg&k}A{5 zGNiCng7)m$?fd*OBV>Ro|4SR^b`Q>W+ACERX0t!0hN#_GpUIiiNq!aQFp~bVt=qFC z8?#24+|Z=7TU``4IN#n~XW8*kdPXzWmVtTwRKVxq1$#`hq0AdAI=vUaOxo5W?ecz< z7cjb7urrVJTmCs%@;1SSxXP54$AX0+?HfFFY+k1>Vob7Yvfq?_>Q1SvpU#&Ns(z1@ zW*uEx90}paGHPzkH#VM4%rw^tS}*cvKv>|s(VbqA&kCR+KEVXv?9F8wo2Fk`qGL)V za#BE+PUs(idK6&0_Wo)fY{RCZfx3te&`b#UKRMTM&MxpKA zLl17fz3%_cO^NEtP=3kE0;}G?7Sp?O|F^|-Usws(`vRuc2a>!ECAhU)MpxM}cs`*| zJ*WETc~eJx6C3=)n0Dr?{+b@*%M1cD8PiEeZs8@jv7wo))To6fzM)0h5ZCMseufgox`_%e%3a>|DgSFnI4kXM8w_ll0DStEg>#=6g zssS_(fd;2To>JqT0Q;yPV7(FQN{EOG8Y+M5dG*B?nXe+$oD|1y!YG(`MzMcj-)(mv zN`$YKW;%1!sw{FIhm)ly7bxVjr(=@GZC&oNZH(2|+N%VLovrvUGnH7w3b7?Y=TxK4 zO1r(>7?V>&+`xrzLhSb|GuNw)L3m0f zvRTg6D43Rf!PX%Isgsv~oP@NN&lUvJqWL`eI+3olE+GnWPdhR+v>z@827SrF3XYg) zpFXVi#t#D&uZ=Htz$WRd1|1z;YlVoo_*W*~nwKIx5f!}>go7!2!+@E2;*I!DP-tWG zx9**=B$$RH)GCBi3i|~9BTo2^;6Y=qe^(dgifn23Z@h?WEG$a1<=zq44I&GfOx zkQKR3eQQr}iLm|dM2X3&7^^seDFGy_w(DSF5inlr?c;4RU5YJEYOn;el{)7gnixXg zZTMFIOitj~eyp=SW&f-Sr^&?@AA~xmKk!)xZ%6H5v;Yu!l*2BW1Ks~lj3ALOxBKDK z^KZ^*r)J)ADrv3{`VdufI*f#eD_&MnrTvvYF5IflFSt3F@PbheTap`1!U;0ySJKiGn94&gn#aWkwI zn;%h(wm9koErh5MFj#Zquc{!KYJAEhSnAV$a=E22UAgx&qR}$v!r6$|_^oVmKXcToGIrQD5DyEuee+i)12_KSgx$c4$!MbbD?V?uDmSw0*sT_s>NMMQi5AW_8owC}qxSfyx8nOn*H#4$Y#r$qmxDe4`(882 z)+9lvd#L^uu_N;CnBJjZ2*!oQ=H@0$oJ~gazAm4ytgK*mgr4pW3m4phe!g0jrknqT z+Hd&3;4o^c=e?i}Yy5Pk=FNX_Au#35yzmPyxxsnzxh9sk4WYd-e|xe$zqfW$di%*! zl4s@l(%Xe4-QE+hi*@qY(6z(ytc_Xdk|r9YhCptD(LTXy{+g(~dP*n-Bg3>e5-z91 zt}95YR=7HOWS*vQJ5aK{&NM8Vi?FQuc%TEN3RZ{e3xpWF+U2cLTDYBdhMS0v#5V5C zRM(q~n)o5h{EDX1R7Yl$8Jw+iDsw(I(b2&Mt+P&BDGXzdHm8-+8D<&P>y)!3;=vG- zD)b$OloM(sP&o~i{x`7l!r-|CyL$O;K>2yp^y%qxuT4v<9E!HmnCUlYDwveeZP7| ziWF)qvl0ms?xa)fbmQC^n`AMhluD*+_s+>|3q+_$Q}{MlDErNswQWM>#mGuG*c1jKx|sY(v?fhZ*sYBY&Kq!(!)8VJ9^YB1JsPZ zsDvLGR!QyW_-a8T{VvmEYiDK1a5 zu&q!JmWGb2JKkf;D+1#$%X3^#_sGhA>LQ8v_@EFh&6Y4EoS{}HKJD^{ln!`;7U$P` z(Ovo%<+C=mY2>uflQR6PSdqT?kpIG*>`4290JrVndx9)?DpxAMtL2Xu_gW|o7 zJ*ghj@|v7uV{6p(`r`EVjc8L$lV}BBi_q)jj$91UDtjbB&CS(Sybx?5aYUqi&F?0A zJZ@mfe)!P1^6eAg5w)iEy@TGp@R83LX^76r`BC%hmG)p(r-YXxae{;!5=P%S_qeVV z(2L%*J-)AnXk>w#>bYYN34)b5^Qm%l0^++WWV&Qb_>nof9|iLhcoF6s6PAk7zkSoX zY%J8sKdG|(?D-G}lR)j85Q>)%huya(5BIiAK{uXU<&p@;cy{zbk62MoSblZR8q>b{Lo2@KNP@RML@PW&R7Tl*@yUgS^8YDO3xMueNXXg1KuFByb6U86a&m(gRqV#UJ}zwp;KubQHNLvy-><-7 zWkv=^yhJ;4pbZizp=UrY%3S|n;^Pb1xI{Ii*^(gz_$z zq*5f(s|#&Ww13UQGBnsi?OFe$u>TYHie%v2rD1qNAxne|^B< z1sdd1W4B{nKFbn15UXcKkK%*`V>smEVcm|Qxfo%${ z^~FVWe03^QU!mi3Iqgw- z8gMwnk?E=jAsi;VPC|cQha>8EuZ@n9)0LeT!#%Aj?n}viu1uQQ-jR*2o^gR}^E2Ds zuoQgx8`leZghJ&wJT9&&vP?0l@QxQ*TN@(X9N6tecR!QXhNx=m=!GTaS=rM$I{!ZM z?T2uJuaIEdYMiK?*i@y!xCPH)D^lRpx(mUf#asfJU0#jj+5Mi?ZX1So}dW+Y!e*poADCeY$&T3N!K~_TWDy^0!>BOOPETb`6HrtokTn{L;$BE zzjC{=lhZ*@#ChIPtMx5J3^c3U>l+%HqVtuo5%rfTCCgtpUTkmHb2$tTE98u2^~d=e z0$Huvu)h$vPC9t3krXdp=;o?cS-kHZl&u$}xu4#=lpnRZi3jMvuq&dXIxQz>XFwxv z|IvDYfB?eH3xxYK)pjmdUXLBue$8^ViATL;VG+KQt3poK59LGv{d0AXBvx6xd-o0; zC)KW-a_2Uyh*Q&Q%F0FtKj7di`%TDS+^@B%s|ybmxK{6rG~EgUSGYbb_a{Tfd8sYO z1%{F%rTXG@{!gCJ(@!|d-G&HA>w{P$=~atAbPee*{dJo( zP4|?+u~f@Nt6&>0a;oKK=M|5`xCmdnQo&|!{4pltYpe^`&wKNxhW$wlioNK273R~5 zZrpBq3}BE3-)vhT7cBE-4`=!LKf^bWR4CSwNI^-#W;xc|+Y7WSvbZvPTZ}B^CsQC_ zOnPn0)=vkKuBOiqV6r!y+zX3ih?^nfF=K_IQxrf*sXHn+CPq<9?H^v=#)5)gi&;eR z1_Dy{TzMZhq0b*6p$;<{_#M+Xc%=1AP)G>0%+)s0r!UmD9SEtFkqZK=tJ~)4T?U_m z@p@mmmbDS_fBUon@{@RucMBiWglnp~}P-T=)3e?5+=5sT{VCdyCa(=b`g z30(5G6yxI=k~kFrGc1|NPXE5Lz`zHxK8J@Vtp#4#J>3BzA?-|a*_QK!dx0!-HEhc1 zDDZIa%zjywv#|lyBYZqO)2t?BfB^d6dvN~EKi8N?O^t|XHo4vzxMv>Bj6`3(k#LSc zizdH0bhZh4CuR8qEs41PlsE&$5S|IM$Q?x<3-deq57-|}r~e6OCh zW?njBQ)Sr)AwUz2K?Gh%&qb>3t`sQ-Xm;tMF#( z+}s+uMGxlqk%rFnsQ@$l`LTZN!T z^;Zt-0>$8|Y^kpp@1KUp$E%zpVgrAz)~(|gvff3Or07dZvRuOy~>i~Np`XH&elt@b%e zMVe51ZS4wzgb>P6NjYRL#QZ`Vo&NYF*8YXxr*OLjtb%(mVoeeSx%lKlhcOO9_)d4m9zdHs*9C#Z9MoN2I{2G3bp#lj((a)=}>2&{EV^=XhI6J$u;*XhQ6B!Xv ztR@_apKJLcE;@X$%wZ3LK!L*_mS^f+#!sU|lauEFedcZRij3&>gA+2jr+~L$lDxdB zH~8=&r>H0tI_a}1uRMs^@$lCH!FqG2h{6M*v>LkNma{b#9*sBAW`D5hjBXbeukp8D zV%5H7h}8{mr4A03Y~lUr_1gVP844H@5gV20)dzK3|n(v{8O^G#?uXMF$2Twn8GDRp`kx;Ss66z&e)&-Y#(EP zLs}3fu$Y>f3Pvri+AA1cMQ(2XcAHRt?0;Z7cH>Vj(koQ5Qd!B~-cD&$V|jOuqxOf$ zh}$HzYj2>V8aBDm0S{l!39hIjW$mtrwx#n$F7t_EVn(yU!xwa0<3stdu-DN=yd&Xb zWujVQ7SGA9pim%R4eygBR@0j^+uxPnJJ(A1LY{F2C3>x=$-&V{waJ#sF zec2$B#y`PELX!5%V&+VLhU1FnHoLk}LfVJzyL@hG&9coJ%sQ>?`$|3-(ExN1CK_WA z6oERrI=~v+CL+{ya)H>bMkH2tW)?B0>!TM4+vwYDSAjlWD#Tp&$dbn3f=KW}pVWaU z*|6N|QfL-sRws#Voo;=N9s1B>4x>ss`PAzZ6uEaV3}4u?u#~4ueVt3uML|Ys)wqTJ zk+d{m16kwh^!EMf(ejA{3M(&f7ZmT(Q`m z=(AQ2n?LgJF&A~q1@8PVo6gG4phl2X{k>vz`#Y80u#h*ADKF5oLE`$3vT#;m>^|8I z3L@xyMe!#xyH3c)kB;>kpBTqR~Wiz<{O$qWb$eZ|U(ksk|5Y+Ndt zVmIF>@KJnzxJtlbG=OUe#Qyd$^Nc+IEx3n~rZOL1AUp7Ix zJjkD?not zoKc#U>aX>1+l45nhEvlmhAYkgtkJ{a#`hy~=4A^4s_A&H?c$Zw_H%1|!KXJjH=P+}=Cyizdu4xqz`?-NV0v1(GgTSuIky8MPYZ zwRoek&(?ygR~^hLb{>c;WyaE~;gIOa>7++jj1Iq$CL<$5YoW-_kf%~|AHpKhRHDLa zB58t?B}{`hC%=uy4;$i-SW=CSj>c}1p|CQu3i5d01J3kxgL?zOCH#1LqUqPSF3fH| zd+!qO+OGF~eQldXx%qP{7@vC$hUm00j6H_2MP{3&gG{o108qh4qsz#oAsOoKBl)uaE z{2U8Ou+{a1urj+)q6VT2n|$9$+m0#J!>P#@8@!Lb(Q?a zeI(WW9(gr5R$2-V4iEVsg|uLheB+1*XQdRrUk-Td0@|P3&o%LRRrm#Z%y-~qKeB5~ zmT*I0%8?bi=Vv%aZDY8Tel1ikpeyCiBMs?x4uD01N$bvj}mnJ+O2BJH$Aay_FFS5 zTxny;+8-rYRf)z_ktKNh$E`lMJt5*YKz4^NT^=Gf&BDklla+pvbGBjidV9${vNkSN zwdvG`2XcJy4%LRla-Y@fyStwSJpUZ~cMN|^ z11|X%D)|9}t^TjQlEK`rUn1mIY&~|gO?1c=JV^U-cnUnOEGFB#UHkpZm=3yJ6hj03 z0(fn9Zy4`Q#o?<_l4mX`EwP@7Frr^9JQZrAoO4=o+drDEl*}iY=JaS`O9^gkDsy0* zvEi=JclLDUNB?YN@_%Aru1$;LwA;#%OA`!+-x|l0ZM;l5PrvS8cs{>c{xb_;;H9A# zwYLXmW!uvCcRevVs-PlFh)N17x?o}UO6!rCppp+LkCMNSqhlBV$lCOO0s}&TyqqVN z%gV$iKn2wSJzd@JPVcc?AgWQgBH!G^n@>2BCFPRz3?FlzTd1Z zD1?Oi&bu!a;6Wwp;jC9ZJvDX1Zpgr{p`(-Dcd8L>3t3EuSFi5t`GyjPbl?O9)&qgP z^iwZByhj}{3R zC*TAUzjixg3PEg*T1G~lgSx`br@J7!pqM)E4F6XAGm+gA97MnR)T&yK#FAc@BD1_N zg9_4IozuO+mOKSowEJ`_b&rGv!GGpqGKy&Gg)!GMTQBGz0n`^R``x$8jaG|qhR_TR z*K^-zecT%?>+&N|V`#-!CF}3ZJM#Bro6PgZ`q-_%8?YExsnJt)HsA?q`pD zS<=@bTVg0)YKP_^>ZOeL!Y+xy%4qomU$1PX0A7aUL=Q7JwPNc?Ylfg`_UC62THLvs z^72PnCzZ-K5u0J5)BU)0nL}4x2ab7n7E*)Nb*Cn;ap;uyC(E*B_ejHwC|*!T z)4YG!M?AbB;vW*A=%9+_%no$~#6=BQWU(o2o50EPZzK9Z-odEH8TY(UEIMxr^Eo-E z)&9>F(a1KpgqWCs$hY$H@{9c5-z%o7vwbh2j42oSUT1l^x3AY)lUg?2>y(z!m;Z_F z+OHOTZTy0s8K*zi3-i@(>qD7=AjEDRC$&-NT;-6W*c!e8l%Z8Q%_?F!N`{7`R#L_h z$`J|*@8+RTCl?QiCe0`xbBKnqwy{dN2vY&q&;+~$cm#N`ugZrZ2-l)q>saPQz1FA8LW170B5$T_e z3sHq0B#Lr*8dtrl3T8|7+L^5#Lw0)N!P48$O|yw&UROOw{jXVqkda%D5I@>|OmVuu z(D>PYEX4ayO5N4jM4NBk(UA{Zx72y{w+7fUfeL~^oBseg@Lm5|f% z`QdCWyc%gM=A;?0@pO?Yr75^bc(>D6trC4ibI^0R$5RHm7bZLDY%1_R4d>_%5uk2Z}wVu(nsU*AvvUDYXji!R}Eyg0TXZ+SAna z#TH+XP>c8-@zkD)6Led4EQOE>2lETst?zz4#HLA)JNk1xS#A45j=1;!Bu<`#8Ddl^ z1Z11_AR5ZwHuppJTg=vCq#ji_Z&U$=Sd8oS6zaRuXm5(juG-E=$7-oD{mrC!u8)ik z^YQTT8eDMw17!1P%co)6nU$GENkOsx)M49}YWuHRWwk41ei!ag`eLys$H!%5J=roe z5O#Dhn-4kbZPNVFtGJP5w=)$ts2l!;PNVdxQ_{eo*_GEwdFZ!Lh+xX+Vzs+4$kqv} zn{6NAv1m4)bjbQI+?#AVw>4}dNl$@?;ZmDzw%jl9=>f>)yGof)Rjg7jbEZUJ-+l0<$k@-k`TDZ<-Yxcqn#B)TEH%lW>@*jUJ zy0LBOG9n`}$GAzE3HxX%C?1Sfz)%Xv0cbUuWE*1re;X0ZUe@LN9-V_jLvllM!+%}y z=60Jy6hHxL{-C$o19>uzURcQj*4o+jh{`@nK)i#Uat zrDtrlx3{O~9UN?~2mXsW;bb5z8?UjOw%j&a;j^572;0dAcsQgY+kWkm3HHCoP#U3| zHJN~fX8mynMB!;NGc|6u`(Y$PLP9JCy)XqeudUpX8WbOMPmeCkg<^Q@DMwp+=nXAe>88j;Cn#~y;ef>`S@~{F4A+;L{9UXff8DVU#HR1a9MBUpm>7N zTbSzB z<#?t`rb{S@%cyPy$-LNX2SG>WFFq)P$T9S4pI2~emANzvRDo>S9X&X=e@aWsMXUHD zW{jQg4Kj+{wse*0x>ULO?LW8htMq{p1~U(}9y_`?E%#QNbw%)d|JAji{y{H0h7M?E zRt8pi)hia3Hj=&H%SyR6*i7}#jb`ob&^ZLJ(vA79y52xv!eftuk|y|WozJ^h(aD73 z;^GwZRf(wo9AI|r(~?K~-J-c7effHRsjW+zf>9edR`?!Qcojz5=6@#7&gz=2SCs4G zva`8#IBbz@Vbf^&FM^B*29o7PlX~5CbIXg)lCas5Kd2uw<(}ORd|D7OMH*)KJ{Rh< z{0xi9e6zt*1D|Yts?JF7txW5WbFPy@Nc=`kc`V`gJ%7G5i~MS*cKeiFU79i|mt0m0 z+fVxBUl!#D74ukHUlO0-Ds)x%cdbC1pS72h%-Ork@wM@`dVnX``?~IX*_g z!0=*9goFp>Xf%L!T!&V=64I~XnFVORt6YWOG$xLe`#aMp1h0&=#Hrk;L%aJju zLsHH39(6Z4F7AW*amlkg-KvSMm9L7lI+i0}gpg%e*}ACIs|J5TI3;Tw2+Gnk(Q&@M zup0>j4>HvRGD`6FA?{1omsD#TKj2>DCHzR(Y-D$~o_5Ug(W9^rse*!L2b9nrKGAx@ zf`yJ=V%uS_y=|*Ogz6C59Zj=0Uo}2`sj%4WT{Qp0a~<2p2{sF8{3OpgJa&_uj|jai zG^z9@CHmwM-{rmeu2eL)MD+pO-eF?;Ir6xSDna+}n+Ef<(jpy5trOv9}`~%(m|%JRp(0JXsjVu*-f|@EqqJiy+3|Ht}~obMr3}9St9v4zX#U61WGc z!sYtpXfyKq@iO=7k5j!Kz~;Z2}zuhv422jan74o@w*&#w9a}dvwnAa?uUGY z|Ka&<=Fh{VTY2uYMH1ZLQAEOkj4F`%%*P8eKlnPNFJGQY`YWgZykdd!2_DV`dAY`S_s`R01CjveBJ{mjAujEBdq=)mph|uKxP! zYOL_QOzLZm<7>HyxC!Mt``x-~n1l?em!7h9a?wOF@_tzO`IXE5gIxp$Cd&{@Nw3IO z*tNgg*99O4!n0Yx2YI&CI8tynB9>9@0tz)78ym3sa6R8{?SdxJTQTS3$pun}ml%?Q3loc{YH=p#0+Ja{s(d)=t3Pze6c?yipm6Eo{jL;8*>+G7+#)~6^K+79Xx1^k(B#R_F;4%ZpOzS-jOo8TH6 zzIb7%9mmUU+^<c2PlF6|sf3fw1-5h@5T{1li@Qu-~ z3UquH_wC_qOCS_MqhT^zYreK>ex4j_^JUDP^?tYjb8t+2dq<*+J%4l_xb7!Nv4zub?fTMR-qBm3ofLuCkAks$c*5PN)|hCo0^>Pwt5A`H-LKh)(mt^TaPM%{ z`nPW7@Neo^3Z#d9M)`jVjMrC@ZxN$jfn)jdqOP>Ea;nBjJic^QtIic251lwA%<5u9 z)Y{Ajs027`YmQf9vu*-W^jH&GA-Bde z@pz2(7r;QN9F*4L!>Co++dU*#XRG)Z2}}t4%ALtF1iV)8>(riZghaR;Mj10E^X}#E zM(n$bmX@_zC@K@zF?KgDQHZ;{M;0-bR#o8&b9=yM&fkBts4+zN<>|@JAqX5z<}*;= zscsHsDu`*LrNE*iO&@+QA#G#tJM84 zD$((Cr%@;j!din!)mJWuYR3cqgQd1-JdV$mI%OYQPS>Ek#*H95xQWnFq(bLReyVMX zG)*F&93Sraie9GplZ2$JaI4kCqk5OqCLZU*GtpG5%)}5uWi zT!!cTkOO3aM`oLtfY;gE_25R2%O^yjBDE%`F5#NivS4MD?!Sldq_tH@ciyS-keJ&< z0xkowbeekF#?&W@kvX9c%VBB%jb0p>! zwb%`{k$W2I>gnmaN5}d`Im!{`GE;(J28Rco4>x+B90qxZIDv8lYZrJr{m!G;pQ7MZ;R9nN2}E94;L#L zE39^V&49a=md=x-v4JQI6+WxUQ5%gxP{w5X;=M5!_Cdk zUKw?PXyMOU5A$q;$#*)&WY}J(>El z(gF6?X<@OEQ`@*d(}Y(V#T6y*mYn`f9Rts5Ntw&`qWrr#PttVB2OVec;FF`SVZg=* zjN|_;f8E}NxglA6iH?V?uPdgnoz>}M!Z-R~bNyx0K9BugL%}UVh)u}WCkvle6LW64e#lRrQuAq)qQ-O52)e)i@mpus;Yh4eia231Vl*@5D)<= zr5mNYJEgl8C2b)B0wN{dUD6#A(%miH-Tj{aZ|`UC{fx24*zbpDkFm$t>x)a5u;!d= z&Febr_#Hk-?cKwF9MplK3SC0*#2QJr)o&EjdrRT?9vJ13jxVV9)lbDOp;dSDMSfe0 z=ybI?9xADAi>@HPUf(-2^gxmc*Pa}WKp+%Vj9(jYq}Xe^8sXAyBZ_SM9q;c(4b1Sq!9}`Cm``OjSpES3R~RZt$x?)pTcIN6 z#y)g%VRRh`e9Ph~s=FV&F=kuA8CEgVKKU+c%6)&&)^90Pu;FQtu>Co^4wn@HHV5+i|nsU#?7HlLTFBOCJ5PeGJ|wig+c4pAQ3UK zBN&a+nA5-{7k-><&s5E;3lt##{M`IA^(;>c%~TrYsuE5vjk4FgtHzB=poZTZFZB$# zrphY@o}a+kK)L&fio36uV6S}y$|z6}qth(9SIN!(L@?p2 z=ashk!0qcs{ZNKZh|wJnqgGf9oc0u%Qp`uB>@9Txm_rX#E#J13OEk-c{P0M4?OTA0 z2^EXahuf)Ci{|djJ8AFCbTrKscl(XI8b9a?ct39JmE7KM4F;9`W5s$T^1zqe@5<6V z1axWlfH^!C*Cm^p=KZ6$$>xa8Ig+0@FS;vh=)e2pu;My1$JWD7ga$)SdO@3 zZ~bgAE5*~K9rnQnVc0mHRFuG=D1t4w;KFIJrPC*=YDlF4Eiq{?jfIPpP) zGL$#{l8K-z7`Ur8j<`rqE#4iN4~rY}=jDk7r+rTNVZ^v|y^HUUx|%x>xlVZKoZa*C zJaaIU6pvmzvC%(@?e6|vadr;qm5|oP5Oqb|p9u?j^{0rPhuO6h0Q(Rihc{qK9lC}N zycQIlO`k@}TfCe$M_@JyeFwAj6#x8K4VyB;l)qaO5EqL{@0xx!Wrk#`F&Tp%`UNB7 z?&*O!3=lWP-I4TZ$%Zi3)p6q?xce;`OaDPW=QUpQ^T4kRoRpLlztHcx_fZz*)MbT0 z?eZNKK=Oxk`8uVhm(ZRHqZ~T&hXsYkPy~zj_cJYC+LpbUIhPLDLUWi`s~>#7irKS5 z5Wz<55gEu{uML)nktu7)0vS zD0D4pnA6syFy(g&Y#-k*P(+Z{{F-S%gjJ@P4?z3+|E>>~9a#i~x_ z?P29KN20X*(o9B@!ROcnbt!klKcMP!I!B}O03913pOT_=O&Md zCn}r^way%8Ehi@?Cv!DxCK3(Xk0yl+3V_q*hb$0J-bE1A`)e%Kyfhc)BZj}|=DnLE zGTi&%eN;FYE|ij*>bTjbw5K5>69|-b@+tl&-VTGqY*gw+Fs^|I z7+H3~#Kh$AgcG`{_?#B(NO@5DAY1tyDDx(%#(MEcpw*ulU4zh8Y@jk7m?tm|c58ed zNKY`Cm#ulCkN9X8;>hTC54SxyGBQy;Yc?ljn0 zz2MQEpb^9uWiJ?LNFa>|)=P4wiKxwwyufa7VY9obx-i(-49*>yN?9dOWjaK=dx&W# zx>)02oC;W_`g-~r#b#a;CooR)i2IGKjqEndL2CcC#*#$N7L zDG^>-=}O>$+0&7oJ)C!8u9)W|d28meV?&BBnVm?M@=5+OgxpV_vEG)iavy>Ao zu4-u2;L&T+%0Bi~H&ZuK7G4{&B$=p9^AT)%e1H79Cq0&FXI$;e#LLCsdv+`j8!B5q zV>Nis73)>XU-^gm+&J_RA;HDTQ4!&>@NDwM$tQybLsOP5)>x$Ha*+22?32sFwC?-C z-~J;Wy$JjdCK*rLdLeV)vR#<`je6x-eXc&+EqhY~ebW-N@d)>{P3BCcT#ti3fAjEe z>$A%w%Y}j_mCi?xgy$Y8cZff&|nrldhU7t+c8FcC`8#h#W-K!ZU zgsa8+jR=SG@-#|w^L;X&yEup0^dH!VuEH$k7Up^BHY^E&bs0V=_reNiY&(@%7N1XYfLb!Yb_R@7F1IVF}{^Ta@;3 zv^CWd3$+ePq9|2-cCCZM&Ztsrq=6*+HJ=-Nes}LqZOP_8(7~84AO2&{d@n(Bv|un( zN#u#|s{2|P9az?AW*o=Tv@`d7^@!qWpPEVl> zKN!*ZRlR%Ya(;XVL17{DXi}6TC{wrm2T>2w0s7scJS~`O^M<@z?-tkRY61A=RMu{%wsyMy8|9aV97PJZdKw32?cb7)%Li2M6@8 z`=+}g^7JVcjw^@s&lsPrPW~|k=M{ma-iCgm%HzVN@#n$naAB)4&6G|7;JU=UCPrJG z0jUZVHdj0x9;c}Z=;iyA+Mkcj{I!;*%^9Io{4DN}Vz<;0w0wIwSC15&r3EDf);OIG z!BV6eJc^jcUpnCN8)&nWRXA=`xSz2`7oQ%k@tH4v`SIh&l>6!JqZ%99cYBx*pCILp zJ6UCM85?S-sn1C51cWIo1K(fxbZ&0W4h%rm3XP+_e7DE5uEjor7r)4smG zPNw~xI<)QXUG!K!*H1!&>-K`(cXn*O1}9a7wr;g|cim?=4HolKpfXB1TOr{k^_8EN z68iEbzs^YAwyFh&;bE+S%z)2j|8_xBZ;~)sU@WHtBP+Ia`3DqqVM1a3N*gG1mUCc0 zfx%VyqX_gL=rszSkkgPu4((EWH0Q@+HSwl1k}p56c1chBPi(do9~^m~hV;~!g0e|t z9Q#~@K`yfoegB|K(|z}MxwTqay=9bgVZl9K0?H>TWDT4)r_w?1OFTOW$h2qE&>v(^ zxVX$TMjr?W8!ZNq4|^Y(VG)HdZ$20(>N;b{Cn-B^qi^Y-*7HVv5L8;f+|IKr z!IX0zL<|@&KG3~w$%dVK))j|FT`^cB-b67kb!s?_;|X)}>v!*GgJDcY2-R+WI{{$Y zp9jfTr~U{W0<+yo%jMi5vuw=AQVE&^6W<{RzS4 z@FJU7Mr#~gxh%7Dnp~-0XMC--nD~((9peHuq>dL9Qv<`p47~KL#CCyxB2&E(UGigi zO-yLd-n;jJ6Dz=luXb2BAwhR0;f36bNtgW|O>!T{@6%nYrG7XK?w~58Q2INCOotd7h=gPwJjguxt z`)8YqWHC?h+50i7gL)$ijXZ%It?)Tp+;iJ+#|PJ>k-lexZhXv13LPCy>Wj?1vtplZ zjL)L!Y%};{-wTgK5=kRrlz)EikFPI2K7MKnh1frbyzEUD zPPs;i50ZG>KMts{D&aqjctY9q@9VdMLa_bh1He}`7sAbQ0dQh{HV9U$po#!pguu&_ zr@<=;A;I?+Py)a@S6D_z=36Wg9-*#*!PC%p#0)7<|G8jXpw?qz?1LC*-n8|T@Keb= zD!hNB_y`%$2a0FZnPM#oa{sk0m79uBN+J(!vv){2Mfp^P_=>=D1+8F%82EIF{wt>2 z#{tjX|CtN_uK@6W@U#8@-{S87oKUv+D&Gn3t(1Z5cwL%QWb!6Ijyjd4El@voCUA}6 z&+#Up-lx#X3p^X}uaMy0IFd>I*n@=Tx>Xr+##6h1NNZg&0Q7Id(nmWk@66U_TT4JgZExv|NEDh8K179{riGA z3;m7lJgzsJy(I!{dtUb!mwJciWQd1!-)t@3{rjIntkh4OSMJu;ic)=JUM`k>62{0E zmHT$f-6JBFKJM?YeJg*r^j>|fKT=X|j`@p}m%~FWLE3*`3-Q2z_kTJp;s3isxvBc^ zVf~Mt?f(mJL4QMb)yqq$%Kd1lYpAQYH$jqs5asWQU3$L1hURe{YzXZZ>0gI~jEC2_ zOOhT>3h$r)@cRnyKiB%@in6S1Pm=NU+qZ1%;W3;gS8I8e9iEc(;dr>XtINyJuSCJ* z?B(O*1gZ^p6IB>Q>x>+%-O(y4yze9UEL1G{zK<%|eV-;hT}NqRcYSGTM`2-Q&8l5K zbHX(*w8ez{d~?DOtRT|88XL_=}U^9yZ!0s_XX2)@)#n4Xgc|ESf0 zjHHwlAsPnR793Qs-7B(t6>psaBB#Oifo-*9Pwl&0A6EHL_YG*tS~KUX+~SN zkBFvVhl&}t1%~&1YB4Cv{@Rv|C`VLM386WYEN+o}4GV)Zxpl**3s5xG{XLCyKR68r z{fkdhT#oCB$iBI;u6(@^@u+?3i<_UvCMUn)eo>3520eWc`?@dk4W-F+qpuCS$+TV# zQ5K{VPu1*PT<$7zVn}$)n*QsJo(J6vL51E(wfzag zZ?O}w=3*JXnsSfBTd`5g&8WT#J_{0iqG!B>z*b*aS_)^_LYy~J>()@|P$e_i*&zxF zLJJBC9^Aig$ViZ5(;jxfQ{i-W=#419ib=BpLcE8uSf|Xu(9og{yZW=D_?(=k1q8=f z^m_E8a!qMhGZT}Sc{DonJ!_eaZF*zs>ic*Cy$U&zgBb?-l~|@nfsc4#xQQOu$BXQR z>n*a2n8--n2M>b7!>wP(doJv9fJGc3;aX$klf_#&xE@>AUi@=fZ?AA~s>(jQS><83 zw@7rl$K!QzjuI;#VZ@6=xG0w^x24n={K^iRjB{DgmqWQe-6TiBMwDC$v)?` zkx|!-iK!hsARETU9{HBqmvKI#cUNNO^=+m(-EDo)4fPy!BF4`(LUvx8o4KBZ#Wu|H zOf_$t&u<5*W^1#uvYvKr^$p>_Yg%%jdBEo;8o%tXwK<%}wt=_zp{TeRlW1aXWkm=L zr0lmah!gONi;7q+an!}_{;dV5o12;n6^m0VEr^G%2=PoN-jz;}{ehd0 zk%KH~x!-8-YDR$Y??Qd~;?F58FT0uff~gU78g`~VB<$@w`V48nCQPDTCf0?RqiXu+ z&)5?z?6(==Bs?x*A>Wf_dEI5@WYb^A38lM1aptKKFQM$y&^GMM=3N}@o;Bo4kIU69 z7FO2FE}4NZ9XB&yXu_-|c%<4oWr>#Gcb9&9WqzpTh!!LodR8_89m#q(stKw>@4#R?ibfZ{v&`>?ruRC*UV(d+C&$sq( zVf^6TNPQVU(OF>F?g8U{l?QIaVT4g`6{WpBKaSl4CIShALqpv+PCNCsQjHCI;=ZlT z>)!kuDV)PX|0*M1LGR{}FHTkiIhpxTIsg~@_xHa52oYSI0;?;fI>*K`E&udli>+j9 za~Ch(PKz<2JmfU^sxVsc2n^qqx*sXhBg!(b`WFjim6{ieTWkr1=-q&O+ zAGpTxQ=PJh_$aqb@taty2wzxTUApfd?3ZzJ69=~U&(N?5NvO$79IQ9Mbb#zg;zN4Y!*^fO^)c*>^tP z-uWe^!7L$f5*i&{pHA%UG{5aZNhz`z*Vfvp!~p%^q*bRS*(+5Smxq7G6$ z=!X*K@amLFc;!1b_<41zr(W&s{;pVncHi9{7m&FDOB4w>m5E~@di|!uTO?DSEuxGHJPL>K46`L`FU)+Fk;e4Y_ZGORe8aYz}^>~_PFnqCO z$N9b97J~27r$?5S?(Xi62aKR&Nuq0NDrQk=6(KfmBHW;byPHKcge7O&GE-3g70gIUpU$Cx|!eH6)X z73`nj`Wea-xC`aUMk-pMKcKwiwfn`qSecT7<6kGwS0-!s>y()1jB(n#`eNHWmUj;o z4K4gLwb>c-$!YGSE5;-4keE=dO6d(HnsE7&rWx!ejdk>IvLfAE1pClFR? z9w%Ir;6Z9182G65d-Xi{x|G{xDN;6v-C}dIC)MFB*xrqL_59do%>h@UQBAFeNB(DQ z3%iL4HmF|Q6cj=URPiJ90{W~+-%82;M&9Q-e?Dt|BO}(z__VZU6q?oXY4NEfl|o?$ zM^Z{kHW=!BssAv8B(clz=Ck$PfDH}hTy-Fxq$vqSB)Gl7- zdu|ESl_i|S1;uocl`-uXA-6Wh&%)m&yeq*KsWicG_QxDr1W>&-gf$joog7;d4>|3( z+d|E2eb#wMRm+k6-r2~@v@mZZ`x0rKk``G*ZAk6UAq#x{UctnrG>Wb zTStfFD6)fv(2P`mJy}T0dx7_dt`2Oh?Jaj-4}_rlZzrLG4WavR6Q0wO|DT{S)WJ=lW@YRPLv?QL{t zYF9tINMM28+r`0LC38MOO;|;y)Z~@Q@JN}7Jn-?b9q)w&2Peg9_i}HZoSZ;VSoGYa z=Vj-e!oSz`lIYKM{p{nTi)WFYGm3n0n>IqyapP2K@fLU?vQknW5#ESoCRsIB_!Y_j z0@W|1`!ZMz2O-pZ#rv|kcM(y(Uv1dVxw7hEp1KE} z5M?014NVW0(^Jh z8d_UBT}?@~)(X*d&E}$2_@Unih_*q(qdOk>)g)wt$11N^zaNM zziJ7Z423l1d_2Q~MAJoQinbg7eDdeCw}1RzLPK@Q({{{K%^#^eb#Yl0xtSTtu*E+k zZSw=6o(}VPS0- zXjkrRXIx_1AL(wp(ueaW0QCC&xrJQh5c(vHnYY*1N7115?ckKv;zrVmr8+jIPJ!0* z%TJI$L6lZbn)YcJ)m8-U!*>fC7i8@0sw`XI1tq-%>j7*6VRiL<3;RE33xU~xP8Wmv zVPtYzno0uy4wmX_`7ghfq2dRGuR*Noy7J8{>rve!1)qeAi}r8-BDGZY^ZK4?Z! zeT0$bdBxii_?2dU)$pKd3r2;srx>E!Ll1Ti&erlKbgd@_WM$m#Cep4+#HZ0qX7mSqwhHygo1=X1c=diS6udT#%ov7KwiAmILb<^Dj3;^|W!&&zLG#2g5d{GWPf zn2&%SM7I&YTho}BbGlD|hQ*Sp(v63Dw87U{GmV`Ave3}NR2 zkBDN|qbXoxZcTgIOi#sx(x{wPVjARXR{x^^9uaZL>-gpLpra#Ot#E0w+%7PTuA_7C zETtT8eL`t}=;saBvqK{Mhq%%@yzZ-`Ddydim5z#;^Kur$0ILlB`u!e0{_NVC1QZj7 zJ9B3HDA3l1^Rhe)*T2i{i(F7pIN@me#ah3a-@XpS=)d3Bc15D0h6HC+^Kcn}g%KhMDu7 z=48Ds=s?dj`Y5ayZ-Bgt&UWcFS`LP0js-lO*SOqQc_X@#245pO-)Tea^mCU6yEy)x;iG;uJas? zt173h8Y34&UEO?_!QCh}dh=8jiVR~Lnmueb5TV6o$|My9Oc=NAtbMyO5sV;;5ul%Kc0_oxb8r;2b2)9M@~wtuYZuP6 zDs+D%aak~viyp|+*(xxM8OT$hFS)9lao)qr*O7m1KYKg_<3a^GzJiG#KQ=-VUHxV! z)3pEBx}TU>EA)nqOScKn@UfGlQ@|V2W83=12p%Y9xNLquKd25C7XGBqOL&LkYBtlt zBKv6bM1p9qcWHB~P^YjC;vQfBS4l2W>^n!zftz<9jmWtB%?`IL#Ko$oryMNQBO@bE zbKLk_sjKruvm*T%w=$9+yk1{q8q5)$*gOyQ9)ChU3RYHWt&WkA<+@8AAk8K>F3qKzhj42~rGaPad~5TQ{7tKtW-BtT@fc->ivuXJ;;LW+v#% zGsY-3r;<|#)U;HYxFnDD$4ONwy&j!oK%-Iv_2)iY<>fc+S`RMsp(@Mq8qQ<$xnDW! z_3uyYM%QJyFiHNc1^CRCH%WwLp~=n{xeTIej5DsPBr~=S4=HDSYX+AfP=)>SSXo-i zJE2$V-Kq`LRp8F-hF1WQZD#f`0iBrn#}y1@6a3vwJdu%Tk4?XQ`_>)JA%d19 zg1*&v(vNQ691xfIUF;$yG;|O~xM?bs;|+bIH>W*Az9l{?v%fq~c`EhrV0o&uVG^8t za1>HIUW=ei)?WS4O@ep?aZe#%YzPe%6*{9bXs4&Au8vn3HBo^bkvuX?)nwMwHD_&~ zXl@h(QK8D^;29T}cMY)K$I5o}CnukU(TZ`5O;#RJ?WomSIh-9@j^GW-5zQdVquBl! zM15Fva<{yRYd+F54fb^&d#Sra?NEZkb#)pe38s3JIC-^EdYzu0KGIswMXB~xA(Fqy z1CIvySZj7RgOZxPF-d0EH3R-#>uS9roQbTx9HR&o_r@fwdi_(gbJ+D@-S%|9{PXPv zc53lF7!=H{>FsW5u{kMd()Wz3^Ef%qr$ko$Vn*)>^-CB+U0R|s?vf7-yMLP(?FYSf z1t8e?PkK}R*PbaJ(@x}xo{|o+Mi9bi{#do|lgnCDIw`XlX}|xA^ezvA_J=Rk$Sj+b^J}peVNu9&$h1 z*Uq0sb4mo}-_Z&e6&>A0$BI4$$sU{jVl)8Sd+KKd&LY{YGd*~y*bVlVZFhD^2J#^9 zi6N>GdU9XPFKh!UrcRf|Fz)fRY3O8A>vdWe53`P~y*)V4nT2MC0`^UE{PpYCvg;ZM z1cH#oO4_r37wFK;BumW|&U;-SeH0+Y>nWC?O#N6@Ri(GaZMS&--b=!SYcOwQM78$K zWyr~v^Y-uFdLaqw#iVG7mpjCP&1tnK+^2yIzwOw;QX;B-rtdwgNL0I00JZ`ZHnMO1 zd!$oOFHzdA1fs6i!NGp5A{$<0n}37XD;@}~?b1;Myt-nTl17r}%tDy44sLElLJo}i zh+EInP-(r3OPJ-*Y-~nw{WnrKYELBa)mD-_$HqaVqxh*xiDjBxQ2#}O@=-^ z3R(nw@0HDUbxkcS8qdS$mzDwvxMn6R>qrGqq|Wr)hLL#x4H7*c*CA0kP`wQnPeFS3 z_U{+|n&d2oEUGCOB~<^-o@M)u{vN`$YvNF8{P!>aUvBpNZ=b~+<261dUEKshGI}am zY^wR9j(;x0%a!<)l$6dHL=gvf&D$`7w{f?wxE|GBzv z`x4?`l7_cqqdIF_g}qeK5%F%mcK+m_`>y+Ou=on6iHX6le}6S3vp*LQLApP?-*Z6v z@>PsNH1D6+Ja7O>+%}`iNPT0Q=`loO07a9f#U=aunH_t{QcBadHP1I~EBtx&B^lP* zD#730(Y8eIAWMjNBXkBa;8<@Wqau4J0ix#5qmIF=VF4GL3jeJqQ7r#$KB#a-w+sDk zPHy`p-u@??5 zt}g}SqNmEMS2IU=VXZg_ulN71({UeQoqi!v3arh}U@cy|$*hGsp^!0IM*+|8BuBiv z0!EJ7!pc_SF4B79%5Hitif#p^)2<1k_2==#`oqMv`}2O*LQN?@Jo%Fzyl@+aC1_lTr>BiZAQR*cX#(Y z!_Lv3=iVaA9!`-?(Ti0ECZ@t=oJGFJq2je^_=CS-h_$A0n++Qq`^nR%j8{r}`Rt^m z?@nwHp|x~RgGA>M1Q!XIiEZ6oU8!0v9W4em<7>m$N$&HS)a=4Urgezt`7%UyxO-9n zs$m>;Lf*6|lPye+H1_xiT*1Xt>&YrHO60vjSqdmErPn6ox*n{CT8DJEwt5__QtZVg zBsm^+#W2(;6+ByR@z~%_Y#<^eY&a*%%mU~<(=;7;F&T{e`T0r7NTeZq@xMaGS5nff zbUYwjbH1GA(0cE&|NAnXDNW{TE=i}Oy3(n3iE$SM!^g+$i)U54&Ie5mc*MlHxe+a& zKZEq#Q>Jp<>B~> z@kFZxW+P>)m?6S9SozY~qy1;r3IEDRQlh7Kt@7v{H{NxZd>AHbQ?)4@!}gDez&9~B z?QQPGoVMoITes*KXUlojqpvTw4^w+h{Gd7)p z0lpHbe$u|a5*~-oj_Y&qI3G#Bw6Pawl}P81`{&ZXBr5mAW00RB3zf*gWWe?^h1b-^Kn4>XCB;*%TH6iKNtqAl_V@Mm(F#Y|ug>4GvtuF-lp)cz(gmglPA5|R zxtq1MH4#y+WU8+hHlpn&M5edy;v1NmCDIFTNyf2mjO@$}=oJ<)QWM2bWvkMHdfWb) zM%W1#_Q=i&H}Ic@ajL5^?<5#Bc6XOLZg2{CqxaL6n>L^@+1U-U^K*6;@gRqs8yXqX zYt%$LzM~URa=KV8rjQ8d_1q-F$4`Hj`c*V}5CqMmnV~L9STi#)T$rPt^H9Z0dKE^f zIZKT@V;kJ!3XQwYS>K4}>gIxJ!x?Ih&0MM!IDf_AaZMH|ncLaz1ycrUsHurlFp397 zmYR^>yDgr}L{D!8b`$MkbP8Kt!}A8l){`ZM3vGj3PM3p_FhE4dG(D5YcQsbzT$rDa zKr>~+cr>!Lx(Yqv{ib?Qo&jPz&C9C^Z(2(9*k-rU2f(176P<-JvAkBpxs$^snosWM z>6C6ZwgzkFVrb#`u{2r77Y6crMdBvijoCBJ0(!BSH?U1J6rK`BeeYRYF*QZf(&Evo zUeBiGa;+p_GB(9cLUB8qiisMwW;X2`u#N98<%@jSou4G4QEHZxmZ!6xqxkI`nSxRL z?t1om1;7Tbr)2fRO$t;IW{FQVnEnAiQ2qEQ`@PnBIu>xKP0h{y(xD80ly!KS{@>!x zwx=$tCaR!_z!~}pgD5pAX>VgAxt(Z%oiXE!G|8!7CRnYwU|<9k6#_;t`h|XKXsA-_ zmdn{D-5_RQw6|sm($qRhFU(;h1ATiRIG-W{^*R{aRNOvtF2=2gpYc zzlFtm!iQTRu#rehRn3;XB6?ISTMFO`d;iV;<{3ZN9nRke{%gaFT`JnE)SMGF4r`e# z=Ia~lemF^(;OIy>z70yQJe^8);I`-)>ggpyHVV_s7@Rf(eHcW%f=j%H#=wbrN4mTarTiq*ZyfIvBMbJbo!E-oE<6 zXk#+TZ^NR4*>6-Ws+R^+n%-YZ)SS=$B+QF^-~I$g%c9V3*W4*a=~n?28Hf9ZnrP!K z)B*afnDO+ms2X}c?x*{;@G}o$0Ry$slL#jAl6l<_tO^ zbKi`dXp3%*VBLddn{rg8&2K@mrh0mk?iL4?1KtDYf~)qnMSL2VI3>l!l^z$Xzkdp7 zpHwA2U0zub^PFc?vy!%QRv89eR63f7u&{jJaH{1vsQ*!p&z9y5fXe!2ePr5C2&IS` z6l@uC)hGwgR&oHWt730QgZnxNPz$|n<(%AxZAm$ub9+_X)sZZgsRXZg-t5zRPkMcB zX#t-zn@}A)NlB?%%DVfv-+&o!s$0)sF_Pu;q&DRe%W1iFDk}5K!S)p|b&-wQQYk&lzxWBKfBb6{?HWw~jtxQa~Rkj%Ik`r82V zYBFp;+77z9x?2|i)&jn7?HzU!qT&S*afxdkfuv*Sq;236f3V&Tdd|sM@fs1ALt@g` z@Nk`<#Yn}~6IBzSwZv-|P{ZzoktTXRo&D15VL34|g0u4NMnOj(Ow zN_zS*j8PI#NsU?$t*gsF^7#RZ43^Wgv!m5P2^}3BrI|L`PZ)9K<-j?li`aU3%XYzc z0sQui6cg^`cn3V>Iu z7+1*Zj08faP=&~t3T1}p3E6UikqcOluW~W56s6DDskT9$3DS}|nlxfnoC`A3oF}QaXjHcp{792Tia`E9BEALz{^#1JUrCPPdDz?24BL2 zd#lzrXfeDcDC>P5G{X*ceB4Ve-e06xdp=Tat^zY8RNPA8179$JHY#FSrr#{>bLUQo zWX26(ws%yWyK#QF6E1M|dHLZoA;7dh&|`q0uxDmxM6bKL8Y-`_AHMP?XJ8AL?{#-Q z>d`hfFcKCKvYg(bJS#6EsC2vx4G9sfl>_&&G@Tc3TwQRmab?HK3|i$o$(OgbRPtVZ zNDSpWa^0NO7kAtsqbJTj4Ndo0OE6IYc@ORJ@C5dt60Zb&Fp@N-oOvo9C$& z-V3~6DKNFPr2D-W$K%dVU>;ZF&b%h2BNMo~aS+7@^9y4 z3%!X_o!XH3kY*WTOd?LjgDBcANO<3Wc>hlz_GfHsZnav)FKr)!uV=>A1VnNC*TX9P z+;ai8z~Dx5l8{(Pv^Q4E*WlA==0 zJht5P*p7InMGaBg5;mP{#KciE_@OE^vcW22bzHVcGp2F<$i0?O8?-iYt{Tj#O^mPgwXH;9RHrFikT9F8WioHB1|1B7{HvWu{Z<_=C{SyKx4t!yL8W59>jy^Hbk46Vq zZS2$YmKQgr-Q}P_#pSeRWb9t?!e4s=w)Y>p0&v4>;43E2`%02X(8|RFdU9TE84*9{L?4lR3oE5+_ydZMhC?K z^Q7SSdPtlT1D;kB$t(S7iq~bRGI}35Cu9r9V#GC_8*=K*o3<*g!V@Z3-*Ro$jQ&Ix+^4_y{ zbZBNj>LN3`rQ56B?-DX<^bTQvOKJb-jWa`eYGkwWQ4Xz22lJ^!cDa1h)YP+XJ zs)Fu-rQji6Jik0I=)6!`Iy%T&#qs;M1ROUR_Gd@6XWiE zxS>U(Y@kSYJ~_^Y{kr!+A?7A^^P$_3e-;Qt68l-{c{h9B-QZ97dgyXmWD3k_=#mUQ z;uXUaLzO*td!?vofRMkRl8zn8?!j%Yifz$L;(J>t)3qOpmKVo6W6Srz5zb0%*t+R#gNsn>7%1{agS-?uLPW(4<;_a# z&$-9k5AN0GCG;11d^iOrg!yDDdJ3z(wyL`7=ER9H#}Wc$_igR%`ppwT)vk^r&5cye zhtII>Hx*D^wl;YxtmJajh+Ri=FVYfzwgrhGI4A}#uV30b^xfqIoh6gm{&x_dFlGwM zli^~X!sh&_z&_~Vr;5boLf}B+1BbeR={qWd&TjksBLxa{_YITF2iP z=AOe^h=3HeUh1?eNCxS(3%=x1*;fr&PrT%aITfH*Pf-XhVFG`PzWOSQQN+Nn3GRsu zBtUVE6`7iwrJh$~#pOQWt^t-%ZVVR)<%XX=H8L`ygM8+htjH}sVrXTD2p{7;^dR6F zA5N?7o)rC2%$l_MyOpN<)4qk3&M#V4-h-OqaRwCC0ozbfLp>iws&9*Tghn~Y+NvW5 za}45jm}j5LJRuk>Y?`=JO_lOZbpC#XByCu4rL<$%YA8OzvXiw+sblDFld^VRWEQ`i zsP2PA)&~}}k`{D+9!oN#_!sUoLv~CGHy-HoYje$gu(Q@K*DzmmQyZ7AuFo-BWodZ7|Il!c8Os#=rX>AP}cRIkCcG=4fRZ_;_@ z^e3{>uhaIDm`Qfe^sxJLXRK`u&)=Uo2#SadXDEbgrQXa`KVX=6Cgkj$`;v`{D&5X_ zmD8a&HD-8LGL~EJY>quOYdDv?>gqWS*V=ixUGKm@IbgRJ zl;9@JQDLK`;bP!pV10Ue;urq(*VBOyh9N{oma7ilpS)62Qy^XVj!)RnpVAh3v@t#X zDyaeB5e~#MA~AxpNGW?Mt|93pqC}?RJn327cE9f4xN%XuHbjVzk4^LIsR;0&4d!q{ zk;LyYQ+mF5&ge#TuWklmK9os7jO%!H$unlYK<6!QS|cjz{J=puA}>i_#9QDy!vI@P z61nJ_@hu+%V?%S#WO~V0gT4#x8SRmg-}rAW)O-1Pp?VpW0SN$W^>Arnp*!-0et#HU zIK9^H(UC^6i$7F5pYz25ckaR+cM2-h%4#85X%nR|Jm+&%Hq0^>rd{ASYE{S3J#vo-N2>)}U1O9zoW^P6l%*y<9SIt`vvXgB&I3O< zQEAWbop@Gu4%`Q0f#xYZ_?pz}Hh9;**?FHBG2I+k3!>Nsj5y100n$gCJXE8gcf9Ep zYOn6>>g>y6gqds?F?-j=Yjp;UW!&D?HB2U%_8VbW${JLeHV8YkE_GMKbSK!?@cBM9 zmW(Sg954AcE*ybI6obbQYv=H0?2Ds|5y}EJm(xb-+906)sjKxAjdeJT@x)UzhtuUM z9b+b49hK96i;u6UCm>PGm0C^3i~4CrRaREUB1GeN>-u{AzkIHOX`GLD9<0!H?kG${ z`C2N1HpvW8^a&Dpz^W=xZm*kv#PFY4#& za!>r>9f{Z!1_z+v9g6BF3`wkN@(m(j7OzHtdxD>; zimEI-5W3ceb2&KKd&8)_Ke-=S{*mZxTl2p~uDyR%*U~cb#GR)`GWR#4h^b(2xpb_Y z-zQ=xt?Q&k&9er>CqWuZNJV}WZ_!ozc}90jjZptWqlc)ea$aB7ur7^bbP9=DhV{!V zhoZu<8iV8tZilDC-9+jLo)EiYOZg2;(f#qS-yJ*6(l=XW9VL$9YKDs)Hq@1P(Vxms z*`}lnmIa7hJj^9Gz9P84qx&;?>V8JmhyaSt&5x;u^Y(<*M)p@jVMf1J)XMdI5O|RT z2kEkh{r;GzF7MYcL-uHN%Fhs@d5Z(@s@6`FS-3@M76meMe{WR=qa(MRn-%MB6kjs?yKtBL%Co9-KE%~BEMP07N&5Kv zq-b}xhv!Adw3jQ{6TZ-Eg#Ia2U81zLxEZQ@dTuN|h4R3wlqcrEM=XkkxH4nwO@-^3 zw7lMI6q_}4!R#H7AxCn7_F=RcLXXbB$g6pY20KnY{IGd^f@=Cj55D{>5-c2?gVSO} zo9?@}d?r2d@xOnUT2Db>`k0A)6(TIUk@+Ff1rsO24qyxI> z>5D!9<4m>CipaqQ8A)p~3L8-#o@T77ia$6Hg%JgL`Q>D{)8l**3QQkW`5Jz&Zd4lV zc9raSd!PqiCJV*`%nQbsY6FR~=(opzlY+@xjmtq>_Nbl7R<33zMP|mowSXE)-LYLm z2`IQyXC!n2Sb~j0C!AgBC_hun{ak!h#43ead2>C&_v*B zYH1M+%F=RDS!_if`f=_xZ4$MM%5-gJwX+V2a`D5xaV!)( zN_db`$t9&bq`Nz%8>AaW1*E0B8w8{q6a-}H?h=+*x*PA;U);HK=g$4-UXJ6LahZ*G zci;CL&+~bDJpF(E6qmf0Px+hvS-v&}<3)iYdQ#?OSwaWq@@N`W`Avn&rRc7{uFmX6=!3dqvWh~2LbU1} z@8PC3SPit?ib~JQAq#uTd}Lc4KdI{$i}C%t58!7uM;jAAd>|Exsazk<5%=Y5o8Bll z%C(v(dq_Nc4EaRbwPmR#47UV8=(suQg>*)2R#sKzG(@0d62#zYV>{dMN(D)kDO4*^ zl9#TS2`|U=4mR+O;=NGCijtZWOHsWOf<=OQBqRByGTIWO|y1-G#kQH7hsUU+kj?p9h=u zS+2zKUv=&>39Oql^91QlWJ-gI%*=zPJqc?Y8yDx>i~p#!>x|}agb<85 zIUW8mc%;Kf)sxC(j0SZT_TyiL%7qp-ng>#(lDkwX1iThxm0FUx(Z>P~90zjuF0p4| zSSQDtL+Dtf0%Wz@m;|z^SN=^Ye7AlDGhzJL_k_8sBmy?{G`shMJ(mfXzNpEgfEurIXFy;5aEsAvs?V5JxqER!=M(fO0p`xF6#zCaX`APS?bY~MoFPT%`c6fpKQ zdENFnUNBAfj>a&3f&ryU$?n86S~RIb$2jD3V;KTq0XaTSg@I#GO13j&iXGM9c3B7b znPJ?LRZY8{{^wAaQ998XVAW8msc48McGA)wAmYT(BMbOr*^P(drRIQjCo(LeTvdX$cMYf5Vla87_a304H73jD zywNo^6|~0@AZ&l|b3wIe5>6;2NxhWrL7}k_d0G9iG^NQKUS2G%4wJ*6^khpsv#C52 zO1EO$EL+2W)kO}Fh7d9tj(BLx|kx#g`Pm^*ABL|yc&Fb%P zInLP6kj9^in36V6$XzLCwHyPzTahlrvLjhHh*W69#oas%FZ@rUqLd%H9An^W=j#l_ zyDm;v=JX_3%1X;lOzS~G$Kqo^I$3GlrADQe5+(e-MI9YR%+2L8>xqr6#8tLE z+k~H!@QR0qyD$TdCsGMQG!T~8LfR-cg>2f*GRrYp3r;KTt_(RA7M4FzgVx;u?Eqr^ zT3#t#d}xz8-3 z&dwE&F@mX%+)vTQ0s;bwqp3D44c?2lQN51+P!>v3`etXxgCk?_h&N;RrxbBmVnPIZ z69u9eX*Y~E1lSZS@Z0QLOYG>jpQaPZ`sjQ(!&Hn^qE;(X%(fW8?g&#zhHJ@jZM9lv zyUh4W03Qni!AVEA*|xu;lykMPwY9ar?y%n;@(SY0i$7n<7?I>sMRp!ErMw#2e$dd^ zfWXRhQe6DK^IRAIG~PQwGFW!}`pr9jettG{Rne2>_W$I|N5!Pb9?Iojyt{sL_~GAa zLRE$xSX>@!T%1K+2V>W-?i_8W@>#n#$(!jb3@fjaUR+*+B_%K74(3_N(a)aNWcnKf z>^A+L!`H%c7w1>UONBbJqL!qd(D&2J-{f;Vxl06dCn1)V#auFF&c4`CEY$X6-;A&F zf+T7iYZypw?8>;|3T3V#1gVs?coMHe4pWXA6_tZ2RJ>^iub|?MsQGxXk#tcnrzf?x zq8fj3c2iF>pG?usN^r89apufPbvDGdF=&1Y1k2yo z9D%^bd!MfO&R3*E(Ob<8o}~h5t(Sv@vvr;+zWMr%XxzL$gi`jk3Qez>KEGE1ZHi}{ zwQA??06NrddS0$d(pcf*{KmB*K44+-iPT8jQEHRRW|OU`v$wB3+imxYgYM~D1ai*S zDjmJ;+7z)J;7LeISCm&~>iVaLD;OF5 z0DtrnQ*C{{R-Y~}s~T#42}}zjTmrB-N$*|R(HF{J0V@KTBL1N+oAs=;aSE^3ucak3 z6Px1PTrCJ|1waN^47&Bc&LdGY2_GWt9UQ1C?T&XoOJ+gPPi9S|d*%%t-oTG$H?6v& z(#1d0ncJ8U6X9mpJ*N-i;yG>cEbv4QXw%e`-GUW9Vk}Rk-Wk>r;Bv04s2nG*6!?}a zF6$7At0^&2W(>=F@5Oc}J-xYWzu&coJY~>rFzZNto8JY=v_d1B!xrOtk31IBp=4Mo zHx=@KIP?X>%L}Dr2t;KJ&~6{>Wkos9OtK=~a8 z_iai62g%}ixLDM&xibZLsuAu$cA>YvNB)e54+bT26tTEkTCdXWWoa1XCJhp#97| z()c0(iB@3bt99Pg^bbWc21I?|oNY{GifT?K4NwSqn#qq74t%YIr-P`jSYm$nA5AKG zj67dgqe1j8hNaMAT&$SuJR<{AYuyInWQW78BI!RZh19)Amw59%i5+We=CA}G7NklZ z7>&DUvxy#wq>xiB+8=6Ieq8E0QI?JFu<^d>6%-#Y(O^GFW!h&dmv2A5mP9qrTz%ol zf_QOi*%2xlLI4)7z)(uoXcyZ#HtR{p(QbTr-o>D^Jjw4pUo@B} z2g9kAZK#u3(RL1wjQEW9ESoocIl5vQNLbAN7LoBfKg{JyH$XGmMS1*_7$=_yofHgt zqBAoyA1^i~Qg-9`u!NSL6f zgZRo2bpQDH$HwN8SFN|NUtPibGBE3P0tlzWr^@`tTXVS~;hkAH)=HyCCj*9DQPBDs zd`SF}^#c-(qD$l#YL6W}xIOByAZ<4GhTY*18NVv`%J&abj^XJ#X&4H`O?`&H!lJ*K z&t|y6Na~MrX~OdN^_z&%+@V)G??Mo3VC1>z!MxlO%RqjMmSf9u(AeLNb@W=FDWnWz zNDB@?!&$bsl(7`&HwuMP>g~;=s7_!p&qz+@jm;eTV$O_ASrx>2g!jB3S6tYjr~dB! z`<1IM*I=8{-P2|G79mnrUOrl+v$DFRzM=57A_c6^G<@w~8M)4t8s;7;k?mM%KGx(l z!v#5ziAhNf_9N6JscLJW_^!X>RC@gUxuZ;;Qho1O%Hkj0+V7`-YFYg8A_(0g9@{#f zcF*uknuzm(Rh>*e&7jAHd%p5frscjH_JWbAn|UAM8K-NPPoanG>{PQ&M)lmevrM5v zMHYQbB7?=WM^j<><^+H9n-zs*3ly`%9xpYAJjQ5NvA+=heKD<|yc!%2g8PT%cSQ{H zn~L8J7IMq!={22~iF`or2CN+x)O(8~`?aBVCgloKN9oR984H{wQ(kdt+oZ_jJl=i& zoLq@Qt(TZ3L$0uUqoKWW8m*!<>mhQhE>t%%CR}P44~1j6y!oqeSt4Jn$N0FwPI~R= z`GU|J`Tp_JW~HzSm(0vJ`fpTfMkNVCfRjbqkd!fA!| zrpNRc8c|B0&CM11U)mq691E0WyNl#+E+yHTD~0>mnGzvz1%VBNi5gHB4jax=|H{9HX*ys4G)gI z*%m?QJxC_YUF5i;snXM)y;G(cE`5h-Gr)R$zz3NtmawiExn^WiZ zpY*M@<^B8#ojJrM1%7-{_a=n!bYjsipZEpo3$yb?7RxHL?=8+mla_q)tbBZ-FS=5A z-3Od9)kPW~dd-PPYbfyu??y67D@XI&?foh&$k#q#2O-I-s<8TL+&K#b9> zG%pH{jMC4?6WM9o3eBqiVvYRT$84;J&wS*fNqOtn2IiQf+eDoc!RFoUqm7#3BOzX?_kMjje>$#)GO&gax|Os_9|4NA;KeW3e8+Kv&YBnYO9uz4qH-DCE7cn@suwPPp^GvB1g_ ztsbAa!Ry5ElI7|oCjUc@P5v*mc(eMU8v#9z#0FCRVs#QL`7K9U;ofpkV%R03akAPZ zDZ2KN)6GdN4R5}rxvTV4?)>6g#pbjRq-jqj?ts76W7Rq)`u3opk<7DZM zPB2aUE@e(?sjPVAu94xHR=;=WxJss=Hm01{?sPYPT~{jSXy~fJ5Fa;IS^?+szW&VaLU6J^4Vggo+>JgJbj0h0NoxSj_r&euk3{?`AH(i0y(IcfISbqNE~K5RhSc zH)#(eyP|1awz}~#Tueay=v)iBW9Tvfxm;_33grJiogXT)gaK7snTwcC+Vz{!s;~uJ_K8ps!QuoAZ^%e zrvI32up=`R2(rGW&6np}DDU$qxS@&!3eJs(w`=O^fLxy6Amw(HZU_iOaP+hd(y;Mk zU_4Nkwd&y?$%m;LB?Cij&v^l4a=%`9QDwWW(D~tU z61a45Jf6N56l{UD9#u=8UUO4*wF@M}OjU0SbiDVP&Rss@aVwC1Fk!57eGZ9CP?+&3 zHj@o?L&M163P-V!uoy}5KLP^S@`Ag{>&Y$5 zC%vf*Y%9tm)GEup@eod-Yenp?@6DHetr?S;kl@LbEa#mVrfb<2;mD;ZN9}rdr``rP zV`S8$AmKYln~5xLW!cf>jZ!o7Fk5wAA5HFYXF)9)ZBI0MKtMS9gXoCC_I#R#AJz+} z@|!nqVb0s%kFE-U1?bP`8E(ctIf=YTcNR?<4n z6QiT66aMtEWr0)_V9TJ+#!YNZ$w-Bp=@mjA!Hj#X8-FNLDFQA6>3p?$1&xi3tAY9k z7F=&GrtvW2iE;YHRasR>Pka&p~KmVr*RAM-v&Pq$ z*&;N%s;sAS(lFVDvi)?XUgy z1)`e<^+7H%HlmX9O!;P%=|EFqDkif1%>nm=qi0JB0~Z(XZ`?ATL%Us@FqCmqf~?cW z*x0LdsDmReE?@$imUz3^%$bzLI0_Q+d4h0<0xDkgU?mM|8Dy@Vy`AZ|8{RD3pq5+V z$gB&9=owyhZFIX_UhW})-3QhYRzlypxP+{oONnw-lG1DWzyG-I`3c}$Yn)VbaWGMs z*6zeJ+WnMP<n!*;UqP= z%+KvA(ddkCN#k4(Rk-*OlT}T>k)UFanZoy-;7GgH&dmEm0`9romlY z@zRV=p|=Z_p}xK*OK_LC9s>_oseVaqK~|c4aWDo>ZZThGvrO;-f+&;8Ai(i_A+e+S zLf_O-G_m5WJ;8aYdh+r$b|q!7 zPi%i?+kD)7S(>fpf7Uq3o~JRPQHaAYjG(2huuiP%rAMyS73(e!CXe zaCsK*mcRN;Q$RtL{qA*FObyuB9*o*bx`=fvq5_A6c?Ula1K0`w)|JyaM(?N2i z4Xg<#AHAauq|JSWM&^#y5QS^@_ z0|^KAYWZ2qaR@oNJ`*}kd2aK`EOTWe9Ix}FiE`-vURsH;Z-=IFs?f!j&G#NArp(FCchDq^ z3q8N|^m<6h(f`G~+O- zfYRt}@@$@BT$Vdl)3e(#-|0=6go|6*nDAMj zJqVPCB-%eyq)EF#%a8{Mp_hEF^VdXh8_y3+QfWbDvb?e)wr;BY=yq~a!j#-5#E9FV z8754h6?D|&3KgN+dlCUAbLM&{X|~LmX0%vqej9!O;-CTO*hP9%FBsGz76*><~ zXwJV_qkb6`^yK~%T0-tEmqYB^=3%SuEqPl^nLlkIM$*wukK-+$ic_MqHDCq@@73sE zay1F`H;VQBsgJ|jV1jDq0>~cFxc0Qafvi5Tm|9)g86`a>8qI%u@f}MNWnZJros!b$ zse3A&%q8L=uo{^g)^Wwx`{RdW_bTI~@NgWzUdX;;dW8%a{i3T>sM-S~Cv%EDTCT3y z=C^YCpNn;O@20Rx>!ezpXxW=RS5Y=mH38Z4YAUtG1LU13>Lo{`FcMzXMiWeA&4dAt zIji+#a0YvIpv?wbKY8!i7$qt1Onc6%5*3E$)@F1{ifHqLv>Na{_-A$mGqSSIlp1oQ zuA;UJO9n?(RY8&*AQ4bu>-crm9pGgKby=T$Uj+J1T(ey1O?E`(+NMjRVap9~g@laU z)Zv(HL8Jny7y=_uG)VzPm8yqN_{ID0^_U*5)z2u@vZJbwy+mevh+Hby;YF<)2vh!8 zyH6%6_Vx$y-@R3{kk&l*Z0NfD!+WJv96+6=)1h6 zdvD#$lf+TG60~mdm&P(FY@$^6qq>#u(wXK)zpUXeS;O6EVx%V-2hY`{7s;GUc2W}_{rwrkz9%JR>Q}wh7r?An-iwDsDG=br^X|0tksU8MiXUK&-%9e ziIDq=Q1;Jtu1#1cC@1n%M7p1}>UG#0ZuP%8-krilMz76Q6$?Zo`Xwo7C)HXrH~dap zgE0@|6vMo;<#d#K^u96|Jdyy*5&EqnWv=I`+)dj#^3?x;{@s?h%QKIz;5ayv$s-EhE7B z71?9lDQdh#a?tG8dblp3P`E*KlJ3w28xtt2UrK6gTX{EqCVKk{;!>yTWD;$^_!J!U zCTro+)YaGEfEZ%G+2MRNuC%$Z^%*PDeONBa5Rps5#Ll3)0a=kU%HQk?(OX*Bq|Q6#GLYf64Zv%Udp?_u|XU7_^%s{&{dRj-p@o20zB|z=pjr7w6&2# zy9GMAp>@mTk2^2VUAo4{qZ?#4ETMFMSZ{M) z{u2VLyyZ>shKv=dHIEyKI;uY;;(7M>D$EEzMc`QIpf7^>cv%LM$<59>QlqX1B1mYzA5AMo#D>NP+3{nvqOo0 zezaAjRH9qzo_SlyUm!Vb=+LKk9Sm5ev3+}Hf$#V|Ptvf0scel+u?+_}J(j!IM1r0a zc2wpo7@wV;>Na}1eQ)ta6I2JK-<<^<>o~KJ#`UY~yClEHyQ~KIEeTONJ-!8Z#4L0h zr84x0L6^=t;5vQ1tsYoYTQh}n78ZeL_L5IRXw-^@g+D$deEBmc81r)dS+uy>@%D71 zwe5;P0B5!=pyD3~L=8}9#ib-O)02^X{N-ega=tqj z*Od$6A`9vf6P?$hAO1+Y{n|!fgJ~OzI~n}708nd#gZTa*2l4M`->4Bx^b-@`6Hb4} zslUj)Ry-cmMv?FH8xMWgobRm+wue^F*je|row-9HiRqH}@8bqJ#W;M;3Z}g6yl!55 z*Z0Sy49?+}67=u=gFpq@-a}uWTWce~!%MQ&9%RtGdpq`9*S#%|LdH~*{3u2mPG9c^ zuU*o^|Nr&#f8iwmzq*e9*N6Bn2WmML`Lx&n{>S-PK^;zXKjXFEe}G)rW)3y>wZVVp zy727uFq}{vgnuB-LggP)1+)^C(!SlL{m*T|g7mpqL~vW{UD}g-C!Rj?ajpNl3H;Y0 zWJuKRt^aznK_YrX=W;sf!R>$F?)`|9b8R@fl~DcP#%>`nkT0x0YyG$7`h;BmIHYR? zoY>$Be~ksy4o6q6;4d#pc@@;gKDUx{)kHu2*Zrg&P*KVML^1ga(m!6ta6Amx{Vtak zof_MF?SjYG*HFgk1BbGj*bq?zp$uhVfR*z!{6TY!SH?s)4%uhq5o6r z|M}U$jl223;Hm$A9`=79(refLH$OQXTqSUXOOpM1e?!DaP`2yAzn@7I796!nLVB^p z*2Vi~@z+n>z^u|o7Ag6|AiX%{xqoIC3TjaM`yQtrhm(MshN;Y6Y0%-ZQW@ABGk--k zUe5mhz*4ZDH^Ln{eKA?TJ=;M+X4)@kA?j?TZExl%C@$PESO4?Q>~@p7cVB1yra!%D z+VIcrZj7CW@^A5<5RiuBus%+gcZGtM!F%ld^WWj(Kj}T(+%fj|n|B`p%oe0wCRb86 zGY#?K;omQmDo}u2o>PjjUt(yI<@Vm*($W0c+v|SGH6LW0Cy0T8F|6nA;UOj_5>0!= zHD7tA&Naf*d`_O*u6guYut0owxOF7O%-R= z+YkYVPfAMh6%(9lsneH^6&ZVsVWk(pO^O$=wD)$6{ko$WP9TGJ;fMD(noqcBz*0a- znRsdg?wH`PFvBau4GM)!WKYkjW_*9Yd=CR8^X{|}-ShgbtJCTDDYen_Yq1^vMr{UC?7Ssrwb*MjO}N@XU1kx+?VH z#!c1|O889dH2Zg5yWj%oL729unX!%M>P2g~m)0h?69Xq*;j2E*3C@;y!}_qTIlt3a zQq?KD4}<>?zItynmAblyud6>w8o`7-YTDwr&uu!p@98k#?R8zxzi&vPDdn@XNk=f( z2(rA*40C}|!akmdh59*y)nRU}9zWiV$?f~n0ukn+6RXj9jk9G~k>K)evWyJUE^K7fU&PR6_DczL|g!N{d9Hw#}lb?eZhc7T=_>6Mh&rk#ciKb)(ye3BU$ ziBVZ$b}Om~SlvH+W-iOUo&-RF2W1~>d5Z!ku*!C-31U*Jw`XC96xy+v9|S3`!=t^$ z4l5ZBbT4;i{2%Tb!vMF6;m*U2R6Ykk5Iv&)a07Ijy+2e|I(6#gw6Zv~ACww)_=*V& ziwH|6GJZIXu%4i!E>qd$VvR!8F4dyI=+5jPFW5U)MhYM9?&4s}>zEpw zs^x2oJp23QoX@Y`)zv%@908hoAaWpu+o6v=&uV24jyS^vz{J@pfU@qVgHrg6G&5IWMl-h zMZcncm$0aCTzuldIs|TJDTinFr1K;45ip?peR*uxJ(tc5k+TF)kL9qQoPicG_a_dM zZ-W3ubbqMh=4elU|0e~JI(K*X;cGD6T3f=C625)=HjL@gtHE=%r1@Q?{1T2t5~o+7 z=D@J|5Lm*SPimiU&+)tOyzZTLgOP-kltiW_apgytI4ESc<5=E{x-pig@!>|(LlUcv zO(Mx+YI;6dw*h5cnN`tS*Vtut&*=8-COf5E$!M{er(c3rYd7F zR>!^lI6011q47A_FxP6YTB^65YuG*TRFzj34|!EtQjt2?aB%_C&nO1Hi@Cc6Ju(FB z!?r;2-{#XhuQ8U21;R*TW0b;BU`Pm^GqMnu$#;Lbmx#+zJTj-JN&OwXe2BE|O%&@0 z?OK4Wx;%+U6VN8L1rqT&_5(@|i3}|3a#|m?nyBj;9E|9-b26l-pyc*gucCtrbGCNB z1g2Z6g{sMLX@2*c6KF88&m(E@tawT{!mf+a^W+>*lX91ce2{E)uiiO&I8*AVT-p+D z*NJmGpl#4Ios3;CDmhZG9qIU{Z@DedjzbedwDh>o*=rjFaPP|77XI4-FPhNyKOOML zIwZ|^k=TK&^;wE;-c20z&!b;ZhN~YvM>JZlb75N6mbU3OVbNHca(ch=JQW z6=#7;l^OZan8uJ>`|IL;@SoK7bhea<^C&1BbLCQBdDyQFqADs%u>MMO?fNF5H)Ebl ztl5*mibx)yT|{k;qtQgh4VeGWp-Y{GPX{T5H-?c=LPj|t8Ru#{#d z`O-P8&imxZBqk=rE4b8wuJJ%4;2z1<#eUz@k!C`Ld#*Q#=+)B6;F+^9GUBb`5jmpY z*VWh0G2<$#)g&z1P-Zglb{eqdck-(p_Q>s9ycOq;!&Da;<%&c%ck^puO$mr`kdGBA z7fX|fA|y(asCalh*GGHcT{OOFhcqKM2>9@Ux6KXBq}!L0ZEEyJ`7s|E^qp)L!fuge zZsKGHJwct(U9mmV(VRq=qUqqa1z<@)KuCIw#8?s}(Z4MCuDUHWXKG?XINdxqhztxp ztSw(LQ&IlCIa#UZ@Azz4h3B6 zxvG&&MO2!LkE1mrex=T%CQ(aMzFtOFa<<;(c+j|O%_`$*PK<`Vin8eu78cH$-^Y*R z6BE(h5v$_H#wKMRGBj>`s{?>e`DesQg4x_yLF&qDx+X4ewZxi-l@m6{1%nV2NUu>9 zth)d%(jWz?p#g;yR!AGk97_G=$hE%q*Bofpo~TJFa1hztzaIpP^ugM1P&R%0{Q|na zt{V64kza_*h@mp%gqCPWuKYXHmO&~F&dT!rIWh0NRjXW$xW|PwE_w&>`5{?;xqIe9>T8Kw7Zs7C8K4P{B&JCQ?6RgOAyi&B}obK3c> zp?7DjGmbTc>FO;D_Kzc&%*kC+nsRUpZqmY-tzwrQi$3jIXa{Q0_BVDUb57`L%TdCE zLw=M2rbV6NNi@UbA%BV-Bl!iM@EWC{N#jjADb-INr@u0Bp~@X%cIcJ~iPO7&=$RLU z+FA%tNRUjHaGnlH_wfORbY~09&+A+lUfhy~LeOM+8Fo$Cgi8ncO3Nn82{*4(o!2<5 zvyoH4Wfwxf(`@8gZ(}w#G^`dGTpxw$=3Ilf43SA9ld)=v?*3j|m)H3bdTkxt5^qmF z_d`kzh++%w>oq%jO`q&q-F-|1KA)b%v~Mm3r9ZyO^K;WA79_jR3xdC3%WMf0G_e{OX z)Ay`DmB-FtZwX<=_I-theQzgS=<3QF9O%koKH#b*IGC~plcwV;C*jTe4qaeIFW8Np z6n$EsUnrGqT3is9A#g~d!+XNRx9??U9hocbb=9y+OjWnhe>;{|v}DM{WqTm`_>fhp zjf;aL3Sr278O9I5&i#%GbJ*GjgHw4hTS^sAMV*3RLBV+Ny4Tj`<-vrK#be0rGigq? z=R#VHSZ1nLzh1~_Xy6G?cpm#L7Kc% z-7TF%tjk+PDLQ?kc8Yb0|Gv^%1Q<28Tcju6#E z{lX;R`Y0=)+55A5W2V|AwpS+~rQK*xPS3_h&4u3iF65|fW!=?Z`Thns*NdoVGn=VX zhZRC1qH3#|q@9Hi;iVG7t{lmTC~}p2O% z$rP$axhnE1Eiw8_e`k)GEV; z788LP4XVReYZ&qaBSmQfj|d5~Hs^QFYj(T&1+|*~Xuj8|nzE!lPs_-G_&~+RMaxO< zYXJI~bw#DD!t2>SfQ*&GV^FX1C&eCH7>G8yi;4!`LduA7_m&6n2N$)dE@X$_C=>k=_ z_rRQ)TBTUH^YxHP>aE>_bO4m6KMgA&T&KiSioa;^6r;>#vz}u5(_ZrbAtExlSE+@1 zak!9vADxs=r{Zu!O?*!2+3bpIYtNlMcGsf)R-n*%+GpJRw!W7q+{mmC&w=tF;~TKl zrHq(L)lJeE-jto=K)%&~Jn9V738q$QYWeRMM@2}@+yKZY>x>tab6~7YTKh(oGpJ4bsM_c5bEpP}Cq z#}^Wjwz2VLi`N~8M&gnXnHWQ_r|Xz1YZV+`y8C2IBDoGQWChK`27%PoOFEP|2hf0U zk+T~A5=P12Kx-+^S+yZmhJ!Lg;+fp+fsSR39Di9r{G?HFYthR(ssw+PM$AV;>%PTk zo{TZoGuOQXJ1{t~0<1cBELU#skDNoTz&I#IF}@pADJ}2EuG<34&C1FOBB$lr9$;eT zRm1PQi?S-V_Vu~6OQSB|QOXgHX(u){d232Or6U}NtMef8n?S02wEJQQgcC8R&Jp}W zMC7&Ga%)mAupPd-Au`gz4?@lkf8)F+C!-J+5iQsnr zK2c6Jo-rL5!HUEhKll4NKL}H`NW@r1PWu&3&<20S+!T2iO z(|zj(pY=CTb#uF{2};DU`5T%U3D68IP}`0e?=v8cK}tOg;vk5{RtOwWbUJ?FBnE(z zG0nv*raNLFj^jX{L$~!5AAcfOjwA)E*`l(b;tFJq4Q}VEI(x$bk&s}6DZgq4(34PN zzP&T2Z-2lRYa=uyC|>tgvEZ+jl~a}bC19`{!Hlw>JpTT{|LaX@b(}K!84!AWQkZ!w zcIqrH{u1>BI#6}!GYMXVbC8p}U3?7K2&(Wq{{DR;USpWDM5SD{l-8-^h{%y+^<$28 zI!};6(?RzG@YHZRtSGF0H9Thv#5L(lnW-@!(yF!{a;K`wTtZ)g_|)eWFnG?bcm58P zwVAHb&9=bBIByRnsy5!w0i`CjEpg^o;dHZ!g4N!BC(AMUJ=E&FxcLp%$(>Jcym)!B z4I>vjLIIvXH!>1py`wi*nIslQVw`OnT{16NzjHFMNUg#YZ5|b)3c;J|>f^S}m?i|{ ze{8ztuOG6AwBB~VZAJdoG(D!^srgOWR4&A0DMh7dg|7F|0F9N%S!&gL6ScV!uzz7! zLeWMEbAxEJUfNFOIE5Fqp)#fBy1HANQ|ul`=pwg{xhZyAsuF$M4>L_(T5#4$^ju4k zsRZ)6;w)_LOCoFs5N}U@rXwPA6*G%qNHdaHSZE0!&sE1-kK-y3oNH~g_};I`1*+0Y zUutf%SO_sT;o}2Pg#`%L*>0!!eDJ2pwPU7bO}UZtu65uSdS^BG#31dIH!s_2qG94& z|L2G@4RDz!F-9^i11;eQJyNff8;V}=h!Q8}b9~9hmLswsqb#ND0kEZs1`(;A18Ppr z(|MmuFuE#7!iJdY_)(xR#Jj*CWn}ry`bc31o|O2Bz-Bqfd><|R@q51F^3kfY$)x#* zsCVu*rsD6Wbf^}iOlE7`GHp}6wTZAA>p^_kE|ciE(XX)o5;Bhk<<$DV&n`^ImxI8ab5=0`70nDs4lT{$*AK=Gybfl0?+So^fsPgeGL_`?3xdP7dkyxK1kI4>qP&q3EBZYFv*Cxg z6?_y&LXzd~(YlRTTBVCtudW}0KC?2^S#>HlSUEPGBnT|%XKl7C`%X)@Qrj)^#qi{F z2LueYami&S3yKMFU-qxucjX$sletY-Ri&hs+K3g37Z7c(yy1Uv@TH-gy+PPO!^Yd& z-DEagCURZHCEK8~AS!n{Bm-aaW}e>V$^Zc_lJjt_U*4%CeUBo1&$xNyJ{>QWrR8kH zY1-Uv(4p%5I3$Q-_ao$a^UmOl0eOD8Sf_G-y#Tis_oUbzv0Shw_ zKm9g~`S(37tPRT@CzuWig@*Uvo-N%&Jxbpe+T0`(4gLqA(Cc2DU1zMMZ*Zm|Eltze z-OT~uixGO2GPHN6k?4BY(1>Nmj0?3^C{|B<$a>{SX?~2o_#@+lk0s}8$j|ZN5|>Bc zh5Yik<~0lKUK2WV{4>QA5)#r50*#;jJ4jR)_+2u_5Hx=hSevY;PIj@)Th~6lL?-51 zA&w7@j|=tW-1cdWb27!ka;y~b<&j4C`0+2oKbN&3lz!jEcu?nZ99mNHkZAOe4su5E zm(cgQ5WtG))E_||*4wMsbBP#Ov~!!No!Gu{lYqrac;Y*h_KB?4qFRS1yP4oGE!Xga zwRrYJN4`m9p!Gad4rPF8YKwTCAZp(Bm}vg>yKgH9NWE%0ox}SYF?kTm%crf+W!1U0*X`h1XW5UORbg2=^KR*6|ByUK1 zN#%Emk(G90wV*YyG7Vo zSSvfpEm_=VaF3|+$B(~a`-G$!cSp)ZuLrypK5EuGTh}%5dYw6b`{v`g zVV10nTfv({B1ah5zWT6or2lLGZOo^CM!(s)O8*pbX2c}#@UhZCSKvEqAl5#>STv6= zh+iF8lOX7(vc>QoEY|UG@0x5s+e39-o*kRYy>aRcJ~)7_yyOEBu%>@mtqcF0)<7Bc zuXCxwzs{vk{yFVtH&QRb8@`osCEyfRHi}1qX`yQ5&vZ$J9?!~Jk#8Po3T`{fMqZ^T z160=N^hIZMJwumz{N?7yRnHVzSI5C$bh%VP0iPtKWqT4D9QmF6A_vPVS^X}=v-ZMX zN$9()(pz&nDX!+esr^;HLOu|>y0yNkfL9>n#WQPIK6q{lWd;pFZ71_lc7$ zqB9j)bI2$Y;}fH4c?{dTc|Dx<8r`$?TSay1oU`NJdFecl1<(~84YT1X5@_P>8XCwu zuN9tzZ!URa{N2Q|ZCl#(x3BywA0He>`B+v;%4+wJry?aQE9+}sY(02Rt$B-LwJP;T zoB8KU8^5I2zQHgXpj9I?FLiv1@%)*MR^=tdKuRSfW4!3+L@E|SuU0%wld3lS+$gA& zK%k_bO-xucD?69hbLXAAU$}w-tECs(deEkfJ!vpSyU8(};cqfZa)nWa9XIR@VsCd* ziqww#4?JVR8JGOV5F1NzQ7>B^??X8b$FJfI#o>Z)|Q7CKK}VP+O`h^k`P~|aLW$}Qty&c!DnOajs?0>+{M0UN5HX&CwSRT# zQSY?9b=qBSK9tjmK!73^`k}OhCjkXCv^-XWSrVjT57cBd=sfn%Djhgi;>%6Vdnu#i z;>1g_jFatApJ^6yLJ0B$sp$11Gr@K3gdWz=iPKHzg~YSl=O3?I#5=wT>Zv*s86E_7 zb5~x5=;2QW_fnNE)h%M;HVjG%^0s$hJVZ&nq77xdx7dqKhX6R(2WDcXH+J@BP{LZ6gr z{krQ7f^29(aa??bNauCE(`HNiyI^YSYz}F;?IFVV;CHQYTOZ932?7otGR6>VA2t^-_Ld-}~g zd&+%7OZ#pW$+eud$aoZAYHFbfga;?E)`J|HXaZiWxy{3T)y4$CMO$;Ki5>FSZ8_8u zqJB1K=t+G&uI6(;G28i^j>az;J~oUgk%zHPB`_88VL9OKoswEoQpVY71aEh(%QjXr z0-A@!7rIT0!nySG*1rL^MiQwK&vH0~;#M`^c(l^jyh3=#xadNW6(^E-N zY3&yT5+_qsFqG)s=|3uG#7719rSO%OBFY#0nz2ZDIE?qQmg&T$ zjpFGB8(7a-LdWoJ5MC=4+eyKG`o2%T;mfmhZvw1ixun{wv)k z^=E0aBD(=5G9d>v^;~rvpp#gq4w5oZb*)Wgj+L9rseRGT zuHCz6mb|0wKap|;}O|bDg~+tHby+-7d@=RGU9oC6R@|9-9wp2Qc)JDHu~vEP6D}x zO?5UaujVjHj4e$$(_|T^jN-DM_Zd)>)9dB<9v2fHa3&a2y)~VOw+zy6x!G&N{&e^ZW8nel^t3FCyev$7Us%P0Iqa*hB@ zHKbfZTGGX0Hnk+O+aKW^bM6nX}Ekx27Q`73fH;}7wGo( zjlE?eS@*WFiKgnWjeZhaTs(}*eN0ZI+f%Iv+Am#=(NBE{bv>fzLBJq7FH=0{J7}!q z-#1g^um<~vvB9NZfs0_?z0k7Hh#LD8L%voFF$Xy(k_Fnr&=r7V4#Pc^sYGC&G;8DQQN^ z#)N{n3ZqFi2#2S=@kOSn>Z!D5KPZ5dGJ%lj^}+8b;2t>I7`9$o?T;XgaE{K^PE1S$ zyu-&x*q4Q@6rU^F2w7rO!cgQRBCt>La1)7=F%=^;mC+&RwN1Y#V>tWTDWkBc-!trX zruzd1@&UpcI^~i~ouLdlYW16O%?LgK=Xj2<7`EXAi_y;CYf!-!oQqya^>DFQ^~j9! ztH%qEl>3H=w^V#%!vdYFI_KX; z0`1c^-ONXYd#QS+L`-QR;S>Dfz2>^X5$S}ZYNvYl{H%_CGsm%qKjXGqY{fgoi~!Of zoqs1cFR#z;UbT}OX@Z!PL(azHCnTUr0F&ovp?>Cng}8T=Ov6L)1Tn-_kKO=IEP7%IzB2&THQQu&>yri zql0^2AnYywZHAi@Po_4ckRM*gCr-OAt!x?fDh{cK$TO{YdG3r9E>`9yrOA^p@%^G> zpsLJyMsa|Fo?uc|*04LOrH-r-^mE708hd{CU|Q3$Klg6)CRby2HtBEXR45I4IyhhR z05t|Z>-f5psWAMQfL`mA7h>FnUJ3n-N(CXf|2eOp^mKZ%Y$3H~yF@o7te zv@%+NYFp_|1=fd&)qdkwiy_^*l_iEERX(CG8NNHMu+j3WXD2%(cNW4nYq&$KzCVVd zq9Px02mAT^LHywh@jzb(#!2U3{~Aofk~Wil_QQt-wFHhPJ2e<`MA?-!5=fweIlb|U zUaqpQhf+*rqaH;)>a_B-|DddFU{EN*G+pARs-yGN?=48sY>ws9Y@UpBncvCHer*&@ zq{**r)uf6a)StV$MK_0wG-M(P7KEL>@(C>{m|pGA1ad41b2g~yzT^Jh{+^{eF_V6c zCnGm&3Zy&R1jKSdU}jSVEq=z(9uO8bQDW)@?D3$^)mvq zP3HpTbbqT@VVu6if=1S;Q0c2J{XQy+TF|+GNFMrjA3@P~!bVY%AhP zOOe<6RA-(K7=t4jyOWuPSucTEW@BlU7Q*p}0~}fZGYfDWse%mt5cfwKf@=kg0P@Yq zP_(zaZUs4nie5#2EScLByvk|<+a}T&^D;-LeHNB3(42dgUc${4+ zMQA+RR`)QhPck3SKcY1Ub~BjHYt#lLtY@QV%$3`a#MT}x zC)Y(2Sjp+H=zLBMx`^Tq5gx&kW$1@jXzjZr3g0?$G0sV}#4`1sGuwK*=SY5^3aXlX zxx9()b!JgJZM;@@JxeBrDNoema_R?tq0lAmKwfk-F-DFV-jGwHq;Og5 zP`$U@v7qmTd9Z!4Ui6e3z+1J0GYXNbNDbSGm#EZ7EzFOZs%ku3!E)ZEBbxD`)~nlI z%|%(R_3lt$B=gcir3Cf-{;40;3OH+Or~|_o8h-*y5t@7$ka28HUHwVRwVCfgW@B<{ zd_pmzG{PwZWSG{{Je49t!`?KF&545_#CN}|k{@nNCn@~cnrbN6GC_IR4XHsJ2O2YA zJ@|JhgsomBNtv3NB_t;PMfcYS5sf&23zA?-D#+$ATd>#B=tq){t ztw!?QKy^k|T`G)1(jF4-1sLYhGUXQIkp@}#TrH5Yn_w;<)^@mJ`q`vR2d(i0IG8AG{U~0KjNU?n5#A4LCs4>XM;V8T={uNaS%}X=Ia)T z2y(zAuK^LCigD%p^+kM1FK-s-0UIR^nHYG#H1?O-z=6tRz*)Tb?z!BC1|9;`8IfP|Y$5LPAAhPZyU; zgL)6umE?(Q(AoNmM{dwi7rfZ^dkzqWoWVsL^q5)2dlalHK{!V~^_6McQ33I7EgfC` zMt21-#p!A}9L8YKs9LI_04rfa-R9Bo2Jk;b@8jty z;LLqvPS4GRBM89iBh|rsTcQGF<3!ymV=0%w%(p;2mEEhIfX8MmqU2sDl;Ki)aZ9ezk2R%{gyKMmfQ>0TK3Ht(r z59rE5RzQc#VfqYEFho4ghPt~`j($!~CO!R%Sqs9)oPmIIHm+KZVoHW_#^&aRLbp7? zPxe>X)-JQ^Ah+qR?&2hfQEtuZZVbE4m z(yOw?q^Cr#U*>$083#{Pat& zV18!VUon=bp!xl(GDRI_QKpE2ym*3K&87Ap z>jTEK^2Uf_LQ{$m6qRD5cTvXfZFiFJHb6NT2l!I(!71JeP&)b9iMLbQR_?2*8WB8s z`R=ODEt+m@Vq69PC(Qo<4{z+v!y3RmyId$G4W;A~U}q19C@}A@82UhTGrHf+Vt79^ zWnkk9K*LP!1!%4FrPMeJF0Hr>67svX9R8vU3R=U;GO60+RdLv@1}(2OY9kV7!TGQbk{I zDDcC=AH{z8aytl8HI<>rvRULF2Mh>c0`=W_-0_g-FM4_ca-NU@92g0vs)}ziTvslz z3$*7_MUS&Xe*co-PvSy+9P-zzF!}w*fCU$YVqW3O?MnuePzt&O2ti+teIw8eWg)Wh zpYVOVl%S;C0yYJ}t<+6ZRu+Qvms4jZuISc#pXsKI0@-JO1TaE#@bC=e(~8rGAN8ry zl0SJGLMEK36Rez-%oH3(Ob!8gTbsPJNL-RGcIEENsMOez93?{-t%F0yT0M`=6gWo! zATQRS`PDRfM3!D{jn5C@T#aQx+zVpW&Vx}(OSdhfelG{->XmmKkPT)UXMo#p8*(pB zA7CP%(t zaHzhrq$1-W9cGJGJ5!q`$4S7QR=R)V}s^O!{J#M$nU_Xa(RBJIm?E!PwPE@ zGQlayI_P}yWf!oTr3t7k`|Gf2fBMLu8}}AY%a7Vc+bJ*fmH6#ts|ipJ&5FG@>R-mA zP%GhF)B9sY{KVv+(Z|Td6f}UC_K2oaN|jhMw^mQ?MzY;=HY<3mlU=8v2o+=!%)BP` zZ|q-hS*{rTHQfK6=v9ZGurNHPm{qmWz)M$ma+iqi_T9VYCJfEv;I^##JR>hBIi=Cd zA73U=rY-DQ0++pkx7|#3=sLa9Y4h-K%-+-=Uay9T%Z!k$=JCdiB)Ry?6>1otKAZ~9 z>89e3Pw5~2p!WUQWEvl$UhlRsm9b^51I4&cTQJ8nJEwQH*CB@#iiQLnm*m? zPUHa|4SxYkgzU5b)@jGe2TMPj$%>c(&%>$r;GVd%K9E5b@NX0OH2iWRK_J2H)QBiE z1O730yT5GqbB?(N=k?I|x`DpVDB%uNWx#1Ox3^;2NM0ux>WW44TpMN}Yx@0$gBGgY$Th{XyvO zkpkWGgH2;2lddCuOLmO{J?#Rk^7Ll^$IJLivrj`cT=xKA!s0J%ojq?g|m{fBeO*_T_YN&f=v~{a?xjTzG{oC}r z;7R!&(k-Jcq4kO*eWl6{J@ZN$z?PhfQmdQF(8Py zL0T0I*&VJtXdxRj`EE{DHUNy0m7JoWFD@pg#>-WaxgYjO{O~GO1PbI$C`_CC zfD0sA+wsYei-5;bh=A+moe4i9r^(!UeAkrNSj_T#VL?6<`XvO7*W#ICUnwOudb4fG zWGk(pV04Bd(#&kdvZwS4{xFcX2214DHT<-vJuUjgeC>UDM^Q=mtknH>(XeOU(U>q4 zR<{GQe1CzKjvUW2JgS{^(p>c>CdwuzmZo<60<@lSUY2OO3-t*-9yQBa!v z$EgkOHw_Ne0z)yzH@xq3f$trPujC)`*er`4gL&#$C2_Y><)3b6^fPR1oNB#n<#x%R zdY=kK2AEF*|Did+bASk%iEz*~qA>8kIjURcKa`gPNA(TKyc@&+&H`NUfA;zjYVAbz zOMYYTGyV4^XuxuM2M%=p{`w!K+y7nw{nzyTQ3Xsh|NYnhFFK~JrZ>D%pu8F=@n@;7 z+-{lh&yW8&@CH1CNzG^rCs)s#tXwxl2|#x?!36ZKScZnVmOeOF>rwu%#w$Yn%_lLo zI7Z^Vx+#`Vg$-f&;WtlY{`17Vp^F)S_Vu;@+t40hrKk7pL{m3^hGRQ{g7Awd*>48_ z{MZYokdUtE=3*0-J9Fl*KVpjlAWga!i-h}s-T=7iipg5h;lG3DbN)Q~NrHN0nCsm? z|NptTj67?Gm_Hw9lz+E?iE;3y|J8pR;`n#6GAO?Ujd3Brhu)t*WAVoY|K}2!BNf-@ z_Y(sZ?0-7IT3X10?)U$GQ_wX3^M=5-djYPnzrX&!KIgzD|DW%4LzWx77jcW2L!<0 zV!v{hoS204Au-}=Z>*(FHI7o50jELZO7ivUuACA++@tEctDaKYZcE6!HZASjsp0X) z8WHI4Hn}h`Fu0yAaqRnoXg_+dG5jlHKq)7%2yPElar<5Xq6`$-wsr3O!orsT$vewH zK}wnw6Z8AR3ORsvajOCH5hf&f?!kOy4_PfbMFW~yP`yeQ$RU*7?3#50JPZ?)zxJzOdd_2}Te}vyiCbD#^&4UU4l<0&8F2glxmJzuUzrzGCS+AWC9*0TW1B6#Z4& z3EEv4G<(J(LzLJ0l)1|{{XfR+6ql~>J|USkUm;;Qhv+S@GI#c%bS4$g*`hG;>-MAy z@0Ys(_89l*+U90Ac%qwpLjIrjZ&~SL+cOp-5uDZXbfkd@>OdA=rT7@;joIpmhSb+i ze*d@a+y?WgI||d(j{v`q|J>ogMa3$%I``8`^{`Ni(hBL}qtT7JmnDYGZ2nGLUn_I$ z?|FqQv-FP+PS?2Ju5Tw1gspLGcEUT`^n6xz(-0OjkSUrZKM$=kZczfB8b75*Quu%ZPx0Km^_!8yFD4 z2ZUA&Epo^QDT1wEr6w!v4C+i6xy5?gj^sgtejS5BArSTNu7ohdgSyUa%J!ZEOTQIF zo|%@szpU|0|K`59WoM_he7HU;BqA!pW(T022b*fIH8qa`JT^O<4h-u^Cfg3m4mvg2 zPV3ycO;-EP4#{6O;-i8n1uKSg_5I(Y2IIg2Ut>f=Eg^(y(c^q;6D05ft@G?)Gp{gj zXXgWo0zFP0g(I0k(yiN<{s5OC)2jD(N3L#BVXgmN-N>2|u&y~Mtl-H}jq9NggTH*) zrz9t53yM-;#5~Rkf&nTCW#E96wo-xR(S2tqkZNU6Q=cELA8%61$jIP_-`k!jpA0l^ zJ3ClkpPNH^GlM3p)Tp$&ag=QI=-WUoc{0&WvvuHBNI}p!FPw1b#LBAM?k`>g>oT8U z?E`X=Wc|mFfzdusU)PQMp64-Jz>%V?!q>!{ERW-7pr?|Uv@3RI2v_YnB zoPlczIMQQGm-$R#&m2BDZw%%Tur>lek*48j&&U>7U4x2Y8uN$ZpFrRW+jIwMN}R(4 zg9rK)Gj-lv;DA~nSNyXWSdt1FExlc?1t=K1sqIEVK7oGD;hiYJEk3|w)^J9DsJ8L* zp+{{JP>C?;21-a>AFd4^ZzhedktH}&o0yt`?#T4iR5EL|axEglczR94!Xdsphg4v z);crW)WQO(9gmd{Dpzb;-@nBwCG+){nzR2QG+Y_~nL_< zfA5zgBlC{sK-HUUOFj%6W%)gCQEsc_wYf37w1-tKmcC)kh$9fLXZqeMOPfW6V|qxd zQS0Q;A#W*L>dviN-uYsx*8T?9^ACBA)5mq8qPK&rr+peLT8hKyU4exx}6oOd;(6nIq z?9=h0L#JzH%)zG4;W>MR0SBsK6&x}F59m$)NoE;qnT$Wib^$}LwP!4f% z8jJ(>?b{&~r)v*YB|+`M+o7EJ=F&1cx{L3M8A(dez?nv&51ET@Vg00OX4XW>r$}0b4UHmtW}>R-Td-JeiQj?|-?{Vx z!b+TbNK+?%J!2Xgb5hcZE}#;BI%NOaYa2KP{v0o|y(aq+*%AyQ8CMh4gY8HpCC=@d z!K?ThleIk&uXD1ePpUNXfo&KD=uH55X6bDtCbj@NQI~=6qJ#vux!s?`!1)>T+t#jkaIETs_=2C+huqB6;FB?||Lso~!2>+p=_DrD&B; zqyG6(7eR({b4SO-)Xxk5hV|v{b?S6wO8It^NPZ_X;BNADM-$EyQi=RhRh_Ny@?WkT zo0y=ZrJJg9@C|!cTVV@C@eD#a;<-gR*IS#qG(-Z<(l*%%?f zNA+4huoT%=eaYcCv^rjD2FR@4kjD&2NL~`JdC!)xNA4BaT;C;&Zs?bc(PvxVxY+nj z6!Z<~cqP}e%s!$IH?-ZQ?nu+li;JnK7y*sjnt+TwBs(iFPw4d!{`RcTOXpSP3Cd^T z_-SE#R;XqK(d_7`W<3)V3E?vmiYKpJS0CP$_6MS7+ps;+iAy8W+pT%AB+5gE zz^k%WqgW6(BD-$BuUxDqzuN|mmCp%Qm9*!!*f+(7pJ7QA3uHiw0zX)c0SUa`6$T%k)7Sz`l~CEI|&lz z9jV%a4BkD#!j~?TKW+Z`<+MY7=bvJ@vtus7k3sryMwd(?%yK9>r2s4|Q|i-fqI#ai z_S083TAWQ8-#Phdx!KvbgKv`1+Km<(kc+R{pn(Vu zghGQen!$TBsAVCxKxa^@#Prc4qOIBLw#D`KnBUarlX%*WvN)!3Y92NTphV0#+1>Ly z@E!q`kSoJxSVS=gc4pa?V5L2`-INyPI|%MtUop|}4gmatVO0gl5T@3<16h`+*O^4U zIzRt7czbzKQHZUNe|Jz#=QN0h5M?nx@Y5(`;)%}5WPcRMP?dA*>D;zb0{!Ja<)cRu z3Vl;u@*ptma(Z@(x$nI8SXPLJCbO%%3uIWv#hdaXjbXMfJfegy%+{YT zhF?eBU}k0Y0u+F2$B&N>*pwm9b39)N2_1BeJBbp%o34Rof`mM9&|)-i*L-G*$+N&m zrb|&hqR7AEWMabT&*`EhbLFMHDxL|uquLrRGF-tB=!?5A(3e$~m280Ni+kTxJ0Z=3 z2O^;o;3N4W*=?Wk5)o~3-B2G518#G+_{NWI=lLEi4|ghUW}+h!v_X!DmTslTu*zvg z(GC`7R{Rz3sR5r0+Z^;`%_ED&q)Ll7^;6&!?7MImLM{$`a;|=G6N5CAI-QO?Ex!~6 zT}SlJyg1;IWfVYNT-f=gmM04hYenoXTo1pIfi?+Yv-De9qjY5xOG}w*B{PXHaXIve z`^&K;uw$k0AU9aF5ZP9Y($ucC$6;xw(gAWD%g%km6>~-h1Jh{wX5eY(LXv zX#mlAcIT|jyCX0%DiVN!rHa-uYXSm%ki2)nhwGvr_0x?0X`NzACeY@6t|-+h$22sE zoQkD8*1;vF%G`i#SM{=&rhUfiOoc@bH~O23y!d)nR-lM57gtn<3Qt(I(|JXxLPFtm zO+Sw+w|DVP)zCi^H7pj{Xynznnl-uuU2Z+h2s;MC+At04k4Z=TyDZ%6z7O7-;8@s_c933w-qE=P>V~KoOzh zg*}cTc2EAVrSGdZ={u4Pj|OHj9`^Nh_26V}vd%3S^bfv#{i>oOWe*S~ms0oB>V40a z`vBw6ZnmL7IW$srCXzkMcIvwUbxfe7*u)SSm$^z}Yi!oiQ1^7EBbtdmjB0drH0L!O z2jtEzEiIMeUp9=%=*t7rYBl!05wQmz44+7_+TSi8=nI$ zq=-n-s$sIw`zuf=u`s;wiWyf8US=IKMe;*lfzFSQB$S+PxaoVkI1pi3Ca}rY1CZcA zwMDeiIX>lTNI-aaCKu!{64f3_O|c2FL!^`xRR7HB{0NNDC&tH9M15uk22h@Ud%2dD zaqRjT_L^-C;QqHm0DyXiQoTnV4TII>_bW!sa3p{H4^Omi_l@;)bl_j&9u@I|>7)p? zM}c}7$)w_cS8Yqx;y5__wr2|MHkr_kRd<4#!t^~omlJawcVc26T}~D8YvhH*tesUi zn-%a=$F`@Y=)0Z#%W85^$t3yCx%vc|H5g(dWGOxEPlw6{*+X|GUSi_qSl&b{$f) zxg7R5&dzq-k=t&O$&cYCA>;OH?geJJ(>%3zYI^F#^XB1XeVvf8vC+7oVC17HhO!K{ z=)%G@K=Bd6bnnFY(9+R4Z%xDqjKwaKB(6v25i=|qMZEQITl7TSc_kg@DGGy;3&1JkQ!sqtwVRTl zJO`i!(%RinRM{AdWtqBnLj$+DeEqnwNGWP9584 z_Dez<6j~&y2~{P4ol`+q>~ON$X@!JUS4Ad>(9#0{m&IfF-4C8vH6Bn&dK=|x#m6bV z@Uw0S8XwpGE%V#CJKLtvF$NVa4hcFq>YfBNFiX2(P{RPRZm^#lEK$#!OM5O5pyrR7 z?B)kDwoF%}K6~F>Emy6oB!9u-f{e7LiHYpj{3ZhsGHwh;#k(!rSkfc?o9aetQrz+K zp>@!*d3SQL&1n$WfLVp*;p`k285GpW`RyHV&v>0LL*;F{KsFS+re+?*{`X1>-YUT( z#l3>`^9z6xcc_BVSln^~P>Q*YiKPxFpL8&%r091D#2{jKFfjzfcT%PygoMPZz$C7a zDwGfeP_!TiBU0tjs5mW9c1clK(aQ|Kb@z`*$zRdx@MY^zG>C<3gi$GJYmwG^D%acm?h=g3^wf z0TctfsWMBL8423qfR*b}Nr3r3- z*DwAQ5dfzyBt&6gbTMy$YBOX*=nU#msBC0rqn!U&i|%}X-~p~R$D!_DdqU~R&#DR7 zQD?-=B+V!$U2L1(O4zV?bL-6!rfeCF}!#ztOEGX(vSGHUsgGyTZz*5*}A;(}m z+ezDItP=&>@E;dnHHFVy!sLu3V1REmc@!Oj~DB?(&Ro{8wmhoq?(hbUAnw+ zgJ_}_Tpbc$q?MKIm)apViVg=mgy~vfNgzos0uW{ZII<45 zX^xIgHL$hTqc%_zcyDDT+gLd|HTZ4rj}fEw*RNM#Eyfe>t__;Jo(DTWzNI&VCbfv= zfQhxWHKrQa$bO7$mA;Ehv<85VRn@X!Ixpj`nR;@f#|3QfjE%)9p%X%w4Tvb*J#vV? z(R(v7v|N3kgw;}Md{+-3__7eQdX^&=PI$VJ2#oEeD<7yEvG6dDAQ?d)T;6(Q^!s=a;@5B61FMm zvL%*u2KalZ{vf-@mdBf-6|kFzb1Ds3*bLG18o7GoqrKZ*YlGWZvyf{cju*wSl#KBO zz$G!P_e>8VevA`DzWUaFx`dXMX7PvbT{TuN^KM5+rt@OiO=IK5SJrD2!+D^&&z5IZ z3_ed$D%G;hQlasW7_@V21A>A81k(ZhZNne5&!0CnXAb*cCBioJ}!LS$1g27n#baGPag;R8}saE21(ms8TZ*sA}yoGf}usWM0n>)95&eDXKdpG5hInOu96le(m zb!5=rDa^#V?f28u>ZJ`Cj_)5&Hz`hnTI{#@4}UuCT}FR*+S7bXjL=L+SJEA-*-CIl zygBvS>FPYHzabA@}Us&Im$T(MCRvHdY@;+Wa04H!U zC)iRV`*>s{dDw@|YtjHw|6i_X70(m(O%$70@t4d3M53!~Qcm;mDEn0@&1EY$t+2oQ z?vW`=1v_Oy;%EIA5y}L5j_9pUP7O0CD&~4pAp`EPNHoX*VNw=;0AXXl_kdMoYjG(i zhs5k_8KpG42!7y6U#7nBQQLYz{1WNadwrc@Gie)0d->u{=nvtH9cV~LHkriaC*ew?ietF6wRNAe3ssc@+bItk(%zA;p(Yl(Ir zg2TC7*bwBI(xC{$KLckK>CVp1Jkvaq`FX-#QIUecj8K3#m~Qm3pU;pyFx}LFf|O^i z^LWYUA|Yz4>6*hKCGYbiRzapOMkVFe8mjE3tKZ)b`DIqFKIH1K>@+#Hn?>ZCEVcI^ zpu5RO2M1MhCu5^h#MhLf=-dt#W`sQeTH1OfAMtD=b-I?0 zBQ}D9lb}{~h(XKA$}{i-@Um`+E5tu6g`r+$vmr9I(7U(*A>Z+9=u1sn2cZ@YeA{hl4Zcfe&{3m|ym{cq5Q5^8!vm4K#J$%3zkgoid`Y%}y zq^hi!T95~crcMWg|BKcB8oUP&bSrF5SLd5SbcL3VHiZus;{H7GS^)WFw1qwnqPdsEA$K`TDJt(6~UV&Y|u08fbXEFU9x@DhK*!?K$ z{s&mUMdEJ+-peuVxXkxuQfpd_b*sL zkQyD>H6$#v29bd#HzQ-&#P{R$^7Wxyy=oM^h)ObwcBS8EccBGzbHEn2%x0X^ntA>5 z{78i>?Up`EYsS6K(cZS<;c^w)@cPYkkVHy$rR>b2dI>jW#sA_ENNk_=D zsYWICqqV`flyzC%m;zP3cBEN!=Ay;BcdyMA+;t|BMvJ(4y@B?*xq(+k$ydo%a*6Wf!e;!I|(ZH(+-4x_%o?5 z{;;j782ru;2S3%=UfS)cLx?= zGXgybr%ah{rK7ub2jBg8%XU0G^I9ufVGw$<+D`)_PKZSj`z!OxdDY9lQ?T%ZN5vF0 zFM4>m$s$U2zAL9IOAs>YQ#eDhqo?DN#SDlj%k%R$CM)g2o(k7MlX$>USfmVXflej= zcc0l`GEu{`a=N;E4mHHXYgP|l4ymNvI|#=rm+$zx`lLUsH&rmM$4G$i_S1Q8KM?lX zs;H<)*XTeNKDd8B@Xe<;3Q63Wap@l;6YIbak9?eVO`1%%m6o21h!T!DkqFBP%hc-ZNIRg#01br`^X|JN=37j2gL z%F?XYnGUKZXYG%eI{W`5roMV5pC&fWd9X5Row=RSXfJvs>M|WnlChwgOqq{|=e(BV zdA64Dx0o8nl=rundcWnp!U30^mh-}JEql4z@mas&u1rEAL3VF@NBxKM)3S(Z*t16H zyXjwXMa^eQ6Q16q^6s52zt85z@3yfCixeLc+=N#;%&H4obMcbup8e%MI@N_#!MT+9RdpB>s?*lt4U=I;|fNSQc~cA7N1Jw z^XCvpbO*%!=aJ9qO9=eSBU)Ij5!lD|XSRKBF6xM3?({AyDpCR^x-AA{Tr$bG5rl+2 zhE%3ItNqQudRC$&saN^_7WQp$fHl1~UP^!XS*Oy@Yvrf_D284E4%U&M5o1tgN3F(7 zPL4vAawas-ix)32>Hw-3<}Lyn0-l}*^{(^4W3$pLBJ-F-*}@2rQ6PkpP`%Wi(wttT zp~7|_J=o022)l7@mV@&mJ-v|SfJZ3UgpRC9S+Je!aK~FEmk%eeV!PW$bL0~^fzFY2 z49YsX308|_WNRJHzWw<3?(w-Sca@QGAwA@8QX$m-EC*PJUa%OZV|m5;*WNY#usKly zR;u~#;K;7yMw7lG}brX*Di4EFfKULLVX($SFJO)j6a5ldG z^K>7I87VFMZUG3*ll0-i;Z6hbcFB=?vS$ncS>jRIkSfv=3`Q|FOX+o?yZgKNYVjpm z*tjI$iOz)F_SBS=$nLs&c&L6JN&CbJTbpdWUgs@p?00>F&QiP_%Y`AEH#X4j>_|eaG8zEZRT4Z>JC%w_Yh)E&>l$n^JsQ1GySgT#3u_#KZ)oMuNVo zA=T05DY-Q2IAvn_{JL#M|Av$UtHCX}3GWup5B<-#v3^Xa^Of-tgR%=Xf|vonWqMlw z*6q)9pBNlm;uQ8$rtddDD)X`>YPdd)iQ^qlNujKEDm~sv5wtg;VU+r5s+-$Uy3#8q z{+Tc+Sp@nhJtVD8;Rh~}v9UbOUy%m!1sHZZ&)bJW3&wz=A6*7g)&OkRXyAJ$b3c$v zU3^UwiL6I=|;}zv6aY63}t<`$vGg1BC-0 z=#P{*wJEASA6RwljAi?ARasVsf|SzX=oT-u6zNqDl*{pE**CJB&h@zSxlsuU{|pNa z3xiyJQi0YMkXI#>4^%b{hE z$JXl>3Azl79?E8B-Y=R2zzrLm=5Z*2mw%-kKta@M_D>+n@eh#h*v618#7Gmd9H3A`n52GXSJi&O)xT4GHA}r%M70OyZzAc&YWMzk4|~f zs!17n`Hf*_P4IEEzpoQ=QLD)MZLlZmh+}3i3@j)puI;g{dF6hq5zd5-hd{&B9f{QoqCfz+wwc*o|-qTJCBSlD1C4g(*$b zWGpyCosAZwjwu=u&)_YzStroKjJF6$vja2foirgF{vt_Q}QV9Fj zn11<{z1Ua$b3euc|+xN@~>JZ`W zb+s`b0HFz|6#hC4a#diw)3j%(nJnP;X#K0^Wr-`tPvT$l0I~$O3WoFenlb2*7a6dcK*{1$$Z3Y+4Ec96?{3V)0B@378 zwCm(n<#(E(2`c z!$i``cp-j1@%M1>rRIQHVRNzj0WO}Tdq(m-Tz2KO8`KaMs&%^ZrCkH?qyx%OzShjb z;$jw8hH;xBHtxi8+XyTc$*)*`ROc%f)up_Lhrz_G8H|IkKcHrWm6g$0_EXcsQ^Lt` z0nM^<$+JPE0~od@6`mYwax|6XWwUGo9)T*A&FbRKc0E0V7xMkX!#_l%t0{N(o2Vs& z%oaUx@$gs_Gz~%RItS4i{YLw6clXEXB3)!FVmGWd zveqSh!MN9_p66{px#d4M_k5|YJr`TO`=!^iJcp0ahn*(C-%(YG1T$L>mzg>}7loF@ z#DpZ8A46U)xII##Nkb_8yDJgI{tC&j$Xd2fij5cAUHM$T{{+P(>)G^7Ep?Q=hoV0n zT2cAxY;OM6FX6@_y_j{JH*ZcuKV!{XIy*ayd1B!VwEYO$n`zJ~*_w7@YZYZoeyb!W zEmb5E4rr1ykd5fXJ$D6Kkf3n33K`vc+uXY7~Iawcu`ipeLtZBxGF4+#nT zS6(({YzLP6t-<{ZtKDyJ8FHofe&JT{c-$;KNncbKsXBl&$sp)FHwvxo&BU@7q3Ggl0*hv@3sh%55C7IIdHUZ(BMX0D{V*KCWLA7jRl9}&M# z$(3HIUzVDMVn%A6nvwAmyV~T|rTc=*TG~%$efHcJ7E->Rxu@ukqIA+FYO{~D+$KvT zgC3Vm)Lrjg?21Ck8pL##cjX8|9vtwnJSBNdw7tvoggm%=V-iuU=T@Axmm%}QN4#|Z zAUDclWvEb}{I@ZhDW*UFsfZ20<~$k5OWd~O4<{*F^lB5Vw43)h4A{Asr3s*5=SQ2N zp@ED#`D65PHWoHt>_;Oa(;@;%EF-8TVcRp)Kx4sMGz+lyr)Lf2B_%&!@Tgf=a|3NQ zz(RxOSa@J47*E>*evBFc&(86_b$~hC+$U7eJq(^}F=1GpSzCo|fG7wWh1g#iHoApYP<_U|)jZYRp9tQ|sRE(S%2p?) zjCW@P#U^F=+hZ_3-rn8=eB-VaKu7~fZXJ`&))pB*xV>8CO#+PApS&WZS4<>^KZGYo zmsz)kfwA7Nhm5(uyLDeldklAV)v3@ zW}=4|zfXTh*`VnxV%gLueho>m4T^a_M>%`NjEo%o{AjMbtG+77TQ~TDj=&8ZoW_Gd_VBH?bKuhBW5<~wzSzy7N_u6F+e-lM)n? zL>2+0L95rG-XnNFCp!zAd3dz!^G(l#b5o0Gdfaezc1yT`13Ye~J=x+pEAi zg;;N4p6{2{01ZZPo#RHpoy*$ndBrd_+EnG+1&5D+oQ6I8aK|xik9<-Z3{(1*|8UfABV%Z2sH^G`fz;=y^@^PYG@DW^wh}}#2X8Vu^uycQRuT9-ia*)aSkg))GzTaYN~WfzOQ*5_obTP* zyd3WBoxVBJoayjQeCd${5|xp@KBQCo`g*2MzVO@PhNmcPumJHY<~i5`vwIb6sR}B- zb$V0Axc$XLeRzpRLcSqKf{DxVZ-`&yAbfey7M$ivvz_7eR$0?5+Ksb-05BO?f?-oc zB0pl#ljl>vQ_UKS3<4#W;tJ{yDY7}GRX%w-NhvWJ)lS@QOS|%#JA=8#)9O=@*-xZU zU!M6bO-M|fXWRcOyb}jnX0799zS7tK=-m8ik}2V-vc6$!M@3{z%PZ*GYNDX=yq3_D zs-cGU1_6eNVX}#tZFVBbGB=k-b#+Rj-QYu{v)4@U1XuKO7)Jf4Tf;gWB%R2#I;c8? zv_^hi*?~@l(mYlSCY3`$5wcGDEY2FYMNAbbH*sOH1Svtqg?T0Kh$JU79YuGHy~Lcb z)iAW$ulv(on(=?<1GGh}43#65pQ=A#DtS{>sWqIW@o$En&i^B(JDx9P|ggfnYs7bO8A_AnF-Of*E2&SdZcs`WrLUPCS27sGz38udcZ-epkXdC)B! zR`8iJ8rz=k>OgV4S)K0OYjwEGkYGiJL{jr; zgK5U*{!Ak2@W>PsGjoB`kU|-c-skJr=F1i4ENvF97Tp1%I$5iW8jB$w-`LYw&BH=u zFSG-~z8L&!u7Po^-g=LKvkqKKQPbtRFPjsktVJseb9irgBw|I z`!My_FX?nvL~L%cR^pGp<pVL~*gx{&L~VtL+#~c8kG>C_EmG3N^a~T;=(cnLOigY?cIcIL0c` zXnYF`GqTTQ^%gUWpus1a?-_LbL5;%r_`G1yYK9(^0@UBHMMo#(*JkFH9amm$`aP}R zzklUwz;}a2#H}t@k>(!H13)%Dw{tc4bWqVyhCkn>+y0w?kRkPpokFQXAw;)}7XYfz zrh#v6wp0_kQP&X>QxjIqx(*sJv9ZJa%AVGF&PAd} zTbA@t8a()oVAc2Qa=-ru^WojniVDC0cFLAul4M6RL3Jy@JkXr}lm2ga2E3gbtV_AC{`ohWx0gRvJ=?bGE zg$AV_*1q1})mEp?#BjKCWAjFG*{qz3S%@}t>2E;%D_dGZ!e_i3|M^z2Sn7E8XaJlu zVnZ4WwZTmW};@MwmYgF8%(ENTObq~Cziad z(?zFPL{sL7`&2)z^m&9m9IgyP-(OmA2)(=ReYD8ZA~joJuE>ysCcaq_j(0zu3(r1X zL8QYNaq*UA9pKseiob10)?~GWT~Yx0jn7Pchz`@O*)}2x z4pXL1qLCNE!n%Dgg3IIMvwu3eR*NgT%Ql!*->~W8|3|{rXnEN#yW`uRA<3$y*XtXPw?BBC7LEmGO%#K?+!G2L*B=6c ze8JVoRmfEd=;y8lv7vTgq04v78=n@paeRz|f`x*P1^#PpV06AI%jf<0^rB1ZjE+*g zJDNEK6ev69a-ZITIXW+W7GNg!J{@28apq2FZf<6=R2j?e`ufPChV@3HL|4ndC{qhk zxN0mY=k%LGy)2-nhS5>*Q;p9WZvpz`wq(n@ckkapL50rw85k1(Pe{m6eRzJ z_IS00mJH9Y%=E*mFxBqhX5;gn=@Ki5vbetGZC`ovaG4PuRdKl*P6XcfQa-;(PS4wK zU?9!AAS#|gB%qEeBrGhP7YDGup`TFU$|O6$M!iq(O&uP%2?*SsXM0!lKDf0kX1IF}(e>`qaU#ob4%Jyw) zrNc!Q@?0@lSb96jY3X1M7*2UU!f}5eWJwzoE~dF(97;;5l3uG$98#xHGs{ z3BXIImx`-PKa`N!Vw7f3OvSIv0U|ezmN(DmKDd9`s&vDfe4ZGsRtPLrWV0C2ecF+k zAMWmDGI^ik9dw$%b-G<(_-vw)5D&lPI!x4S8hBvp>b2qH5%+(!WM$tTZeyh|3o8WETK(ysjEgSvLFVON5={$jc2_wJ*c0v|A`Lb{+al2z}>MkrK zbU9{#1djMc42dSsM-F63AP||=erfB-}dCk&y7!T(?WG-PD&`ENJ?}&+Ng%hB5;f%6 z;m}vu?W?LN8wZCDSQ&#y-id_0gkKaS>+5AUICM7wLD{h{3`83L{i~3GXGB(K?$-oN z@RF-Bpo7%Hg0z>Z=Rb#ekC<~u7C*9OPwPuT{|%mIm`P(%LqNpYQ7I%V3&nze+P^&^ z?SGD);$?7nSc!s>kqMo02)5PzJ!px3Bac7vKUey58L*;p*PtpBo(@gH_|JQ#@By_6 zF+*EvX^hr*+EQ>rZoxd&6w$dKw~)X8@8@+1h5PpZ-RJn9&m9G2+w%Y0SJm=0p}4lL zZfa7Yu9mTm5&i$~V8Qoc{C^%S`0@%PF6*bKjd;9BQl(_+<|d~TVE=RE34BJz##Crv z2p`rPpr+09mX_Aqmm=EiGpPuykQ)&R58hQyY)#8oAtBkx@<0F80{%VsPSew~UW?8& zNZ2jV5PTtnVzP$+PH*tI>;MHTs?Gz$VwfT4Z~px}0fe`-bV#b|e@`9~v);m@&vvH< z!>I7p6i5Yyh5LIrAO7dLLYLYTQ%s;h5gnW(|NaQV9m7T%Z~pVSd8q<~jfzSUI7K$K zVJN{LDYxwZd)BUDF>iw}_05D&6nM+Nhlf_u(y8Fc|2@{wnYY>BgI|&k=#T#2$N%>q z#sBro{L7V;nVFfEL=N|;5|;Kc{oUqG#oJ|3>eQIF?YH=Ze-lu zTTkgh+!_ph9%oYx?XNY3gnBWr^??^e;4HE3ExoMl%Ww+@4h||dcG|(ulED&C6|r8` z?70^wkb8U1t!8T1J<|s@Abtm~;H;?G(y9X+6E7!B@zWx9<~Lrw0|NtVYqGbR-wJr$-n~$j{>u6M z;OgPyX2QlcC!1AvY`dzb8Ss*tUVZ>19^K5}ecLll?JLh-51m?0<;nU=phC2?d+j40 zUS?X{`OJcx+m8EZDk=$ZVaf9e)*`(3CahX;I4^1l9D;1JLBC9UF6CP4$JRKtjfR8+ zovr<1Jd2h=R>(1qfoVQLOP4n8WF;U$T(5LlLG*NQlP|F&878SXq_O zkM14gU7j~&du=Op^jC(4%6=K2>ccTMHob!q*5a$KR+@OTVHL}8b_7kU&Ru!~22M}K zCj|g0XuRzR%o=*%z|?A^KG^n~k#i z=X1RrjO~27C;vnWdWJ%yE3_X5PRdoVaMC^5m)1>wYXS)tm%DjwagmCL_ARACe37)1 zlT#lSGPG8^Yc9Y4`=TO~khjF$-As&3jOglEd*(3FpwtMgTq73Z;^VKbu1H8o08WPy zJ+;)F5}0EXsa1a}0*fV;YNHk~-gY)31w`VoLNq!Sdxctp)FMHoKABooM(ddzkNew? z`T6-#h=#LsBY5WJEgOUh+mN%hl_JjuVuPJFY=VyusBJ`9?pGf;evQ~|_Io*H*8Bi; zC7?GIYu%lL!HmBr!ukiOkeN$E1_ce=`B~0ZmuC6>kz42V zPc!Mf*8AhRJ=LB~b5%Xv0s|3P<I_0n0$0FFzAI6$t;e>P%FW!{~!oWtGNz{Ry7BO|Q<6RPuRN>Je_K*`9! zK&RYV&-||uk*1P{%lR`MrT4YK%zU-{=FJHXmec*OQa~KLA^4I5aIbGl zL!+ZL&koL0nM^~wR$IK_OhwN=lFCiEHMdb8a7;U5VFB+r`&D&t-vV(rNkv8FX+&zv z-85zVzmZsZWq4{nG08{*ZMeI_ZMey3%0#K+Zof44$x;(|XDUw=yMEYK3zLOl(G4-P zOh2HMPTe`7_R&0BvVH$PXwsj;38tNq5e;iFYbw#_VzJ)b9}C$G0V9>=!9K0vbA6n-gn^ONm`DZGy*fFf^pB(x8N6O)ARkmx$ftv*Fb52L zO*T^^x9|xT(~o#uj=kP+r#HZq_rclk9-iIw=6GQRvVue^k<^O;?pvFJ0%^5ny^RzS z0~ z)_=F*OL{$Ump}-RbMWS?HZ!Q=A-5sv2#b=m(rB6-TS@12-Kp_jM>cP~?M;ZSYf_@q zm6D|Mx$AF=^*>@_Tbt~IL#t+Xvb%9`bXG;GHe!qfod{2y9rV0$RN!t{7F}`#P2a%$ zKqv$QAIiwY@MV1NcY{O+7t7@tQqTDkYt3AoW&c32ZpY2X(y3^^>A9KM;^M9sd^Jr? zNmbDWnAj1P-RKALh`bmbmOYNSVeZ`y z5>e67akWW*nfnchiiI?MfDI3gl#^o)~CY0Tzjg|y)9ht{>UwGr@omblgJ0Ln;xxkYkJ0x~#FGHF4Bfd!>kp#{PR z#8$5hvb0(du(F-6CQ|U{X>%S+;W(Hcibrg)WbsU1gDv}JaYW#ggHpImj0Q7<(bCeo z+Un(S`M1W$JacnnbBZYUZZ`H4Y*#~1+9_S#!6Dk>{^c(|*}TxF${s%)a;CaUd7 zg3}PY=eWJ@(LB`Xk_!uq463Y_uE`e{YwT)3c`%7c)W=;078dP}{TpCaCfWhcdnYwB zm~N;5M3@h;Ff?k-avEt_S&+u4#-`+(E1Ws`TNI@Zw@n+>1}bODs2nt>^REC?#_=jfIB>0HTTLx`LV&QW&^Z#X7HHOeR%6Epl0?L08;NSPl7Kdw^pc#9MnP6 zU#D*;ymGpfiM6)g+A)tB&mm-&UY8l9uAh+lUynWe|l<4@MsqdFhS)<6ao7eqrb=rA3Ue3O@0#qFyYe{FQs~|l9m>C zV~J^%4!9lsx;kt)n2T-D?;e|LQXWZ~-&Y`&+ziNMbV_3$0~VkdhV`l?ppU?*Y3g=5 zqWn|@(HpRj&B}TP;NWjzprlP9_ntcH;tjfaVwo}lue(~Oz+F7KOpe4)85~wCc8n-n z_S0!l@Ys0rBi#xtH9|rorKLjXH5)6mB;hPWrnEd2t+g{)hHHHi;X(%xhEXpTdzz)ERZB)mD+s8Ay z8h~+yD?H!!#4ak)Xn!W+U?HMhSRpU*EbZQ~DiRsSm_Dh42BJOlF@xz|eWxL&mF3*D zx89A3V)D^7Qkar%k(Q$K@%<*XOICgjq2krgkMANu$q{^mQ)b#5#5Vk#BRYtY1 zRB#V^1FM08vyIv3@1tPCo&yz6L(l+x&VvpL1Frl(p*OG*0 z87w(lZnmVyq7VX4UZ;rz7_F0oLuf>V5Z7nP+!S6Xixb`sa5hGXG|y9tPgnW)Fh5T; zyI&ugy9b?j{rLVJ-1_rorn_Fg0~Ej}&Po12)M$FsD-N_YEHMSpXaevG*r=Ef18FR3 zw;3%Lz5V+~noS?js;@7u9Go2L8f)D*4l`0?_4RFH!?;OUl=lN>+J(d8NF|Av1U>>J z^GfqPJ0IWL{`E|g%|b*$kw9NqN-vnb8zvY=mwfwE3+2w z!<_zX2w1Bfh5CS&!s%ooB?YfB3U2PH>8U*S?i5CFH^*y$Mci_T(;Zk)(92XT1L!xf zM>@t28VmD{7@CV|*o;P|lNn&ZQ>oI?>hJ${alXM~4xi1r!u=g($^Gm~o5xAz$x=+r zA3h-@ z84&|3cbR-oF6fo8r!^M(-m!J?;5>8ERo;aGx7=k*a87CT&g^~gJLp_OU|T0AqU*F^ zX#;CzxH9(lP_}!M#$rgne*N-n@8IF)mTQ%RfRPID7%)?I@~hQ>kU6SiJb*wpnJ=-D zahlwo=7R3-q>+XITZjGWA@G|;r_;Kv99shJ2L1S0RP8o*1g9uwr0Yk3hO}L6_I>(d zw;{woBZ?&2qJFV6$qneQQ8y=TU2b4_do9?8h}d93WG1MHD~nE}R&7)oXQ18}1SV=Y zeC_7!RR7fiP>hJn*6w$+LGwiCw`W|x=!gi%JqL#|H{^%w6WuBsp7o{XmSz(Z%ID-)2ct)ttae_V|2xH%k<_ZRf zVbJCkR?^NTK)=70e63<{e|~N}f{Y)!B7Aj;;wPK_#oS?UiGC_10iH!58oIozUv2nt^v|FC z%7wF*h@!Kr@?5#XbQ32}flDO(sUhGk$fi)KE(Ex2>$19IZGCW%bO$ z8X_VV7D2hbHfP-`lidA5?}3>}fV<$iyZ$m_$7@Gagj{dqw#a;265ai5u}LTLgBi&u z9Pe@Yk>TYM@k|N3GjX#1_tFO{t8SP5hYTw?b7joNLy&Av<`Uif_9$nW^x5h8S8zfq zg~Jo8s8z(hbB7yMV!a~2R~WB%ox^AG)7hJQa+z*^Ng=q`S4Br!Yqco^|C9{Aw&2j$ z^8ntYH*s-kz!7(MW=Q^y0-aiEd}2acM#j#u{p4h~%c!)L78Cnp*fV>1^}Gp-#L z|3$u8Y~mpb*mF2l%9MtdrW6%ZH7&&E%!!p-oE}uHl&n%~3ndfjydGwcsFV#7=;y{y zuloMQ*G{DzY#NlbKPqBG#H60Ok@A~Qz84OCZLd3?E>*kald1|Htg&Iyax z;4^motMEd}ATL+gkIe*x!t7ZaVcc=31u5{mmnE z6SER6U1@|!t8EEOPr;9lhkqG*DH6+-!XiR|Jvp0ET8RRgkoYN;$D`5lj3h1-&}^&L zs^zWrN0=3PLA_~laWF08INKKnmP!j#)lN^Sy)QhTmtPpE$jv}`V^~;(g3e-j?nH^5 zVEbH6zNqC)S!QA?D88{?#{m@-M+;NMv>J`A&QBUDMQ>m?0f8E&C?#Ffv)Nqq_pjd* zarBCfG!oN5C}M9m?2n*TY7VJCcXi=W_94dI1_gR=p|827?VCMBIlf?}4WN=o;BXtM z4OK6M2-yWJhv{TmcFb5GSTlZ2Z*= zyNM#MS4ovXe{#GIl0*uPRvf=n3cl$ZF;jdVB($Pd9#x|F=13-;YG7dH36KteEERGR z;*XDq8lRTo_dA=-B`PtoA=j6`39^EcsMG$9A3N;eDago_&2q6~?fbJI6&ZWAErCFW z?P2g$;7D&L)bJKy4}e3bd9Ms35rbyfaOUdV7?YJ&l%?+D%S@97K5=)@?R0)~Gq8%` z@x427B-zl=(9Y5Ou@rjL1^ChvR1YmE=y!Nrj%ob#>{xO0!7i*CaGPf=s86HHRV#-- z))JHC4@BdbmL&QAp+S~JnZ+f#BA;77%@sU5g9TwBm=;dQzqcFPaMNKx#TsS7$y*0g znq+#_+4 z3(E~L%ISV+q1Qdx5l;^{K0!F)pCcZk1hNKQ3(Vs-_8t3 z>9kwto6X}nogC=Wg=1OSn4-hOdk9Eyd$E{IK=vcwA|yJzu3Q71CG?+W{lffS?UKiB zXMb~(z#^wAK6fx-ggJlnBYJ8o?2!2m*$Ll9IG#B3yGT{r$FcVZONu` zOPb6x|2m8pe%I-v@soVL;;BAvIIYUO`-%IU-=ypNI@oOvKm*zRqx3OpxeWirUwrjT$fOx)QrMnZeD9W*#riS>+JB#TOx1;?AI_m%;KKgBu1u{n2^Yjer;$TO`GhUNqCDrfu5NvFvk(w;x@{SeFfJn~2p8$ojuIEB z>+>`-J^hY!eSpA6=q)`rjTE+qSngL3u7l*haHcv&plMdG)z>$mZfusTHes6H_B+ig z>mcUWIUf-KwMn(IPD&CINV;25SJbbnie1TDf6st3NsZeKAnltmrL%GieF73y3EHWM zhoa;D$fAk>GDe)%bLQmi?KemW683WI-N0GI&xH#cc%itPGXIG=ZfbHe#+gzn?&HV4 z3>aXB)5H^c&6#f<8aP*~&Bj5A!j#|A zVYc24)}aIay=f_*$_WrFMD)JC!iIp-twm5@RydrP__h#_lila}O{I%IaoTOr`yerb zZ)Kgp9dy`i2eGjkbdQjhZgqO8ky&vE`%&R}uy0mo1Bheil1IwNXtqJzYDT51Er-TqEX%(j|Fg8C7KJZ)bT;H?Eu)-i9pwzuLreyq5 zqA`P*`@U8$BZYCs$;jXZ(}%B11z3mbW^Bg^X<4ktvQ`Goc7A0Be>T{*rLKiCel8zW z5)+m7+oaP8fC7g2zPQbejo-hd*4n)z-wlk7Re_d2P_+aC3#m*VvtxWnXiJa4J6Q>6 zt;xZ~sj2EXGs@;IJJC^L>Mizt!)vOrWkemy`R!Ol&HS(@KxDpkX17oiXTwx|=SER4 ztjyCABWbfx3#ESsh$wb`kB#Rycu?PY15zkg2#N8Laamg1%M04J{9RzZsCVG6JbN7HIT;AM_(16;v6|H^J# zPK(WYIb`^8dju0U>PFtycC{ULT~)$J{rc$m|a#msO#1 zmte#9csd5kiNPPpEaYVdhg*}zBiLB!xV(z8J!Lld<-=Tv>X1){$l%`X;H!>1afi;;HjDp|fuHm?o0;!JM2kE`@6C zI4H3=;c6qfr_{T9UaU`;u0Jht{iWX9Z@y{`V&`v#0#qg#LSe;+ zYV5>w*N1UFg*up&&>`Nliwv?AbTbf@>z;>K#q{`TQ!^Cu`*C1meGaddFN zW3$O{?7&w*|4p5Cd(T5hhheS(bgGG<(6A7z`I6Z3a=0`pvv+wN)0LVo_1nG5(jnn& z%*><1qZs-9H1#o^81MUfg*4UFK|Gy=g%mx~!HcJ()9c}Awc`NbC|Owpr{}Qv$94d5 z`IVq0GQuI)$`B{^XiBniK3+SC`1$*oZG0F$tKO(y>MQqn*I+}Y`+esajB@%T@hS%u z2+tN8k7T!vBNi4m&;#DSf22Wt;{{->AoEiN^6S#eU@L1J62QV--B_j5`Egf?po@l% zb#yy1Tw9ro;G_uz1w;<;4pL)MlnU*Q&G7@E2IWA{ScFi8w|@{=A$3*WUL})Cf-O6d z%?M^nN(y26sq3Z=@JOCzjG;~$qz>S?UHsaB!e#>zp81^YvjK(|2K*~{h+^5V~>y4#o6N1Cdtb6~?uStYD|6%-yF-7|!SfdSq!gIyMgcY%B@N#|Fo z(9J**-t*dwSCYB6M!`jYV&p(1&1!lto}5~#uV6r63+1TJT1M5ClU-I96)(HD?1wf1!Wx$aT}PUkYBObOnxHY{98i+-^yn^49=;h z3?7dnosN3|R2A|Lc7Oj8goMNBvie9|_6`?C>axwFpXgZV{(C4eZr(w^llsKRKt^|c zn%k3vF`O5d6&sZo5*J~8=X7@t2+cAnUtk+%CrP_1G&)4MFU3Pc_c1?#jutiw_P?Of zWj>&Kd>q_(p`f9P3X9cK(gCxVki2Z~4&k>3jjiQuS*Z8_ReVv9Z-=0DL@0h2e{9m(3=*jF51cl@bd@hM2GR#Ad#b znye`O_DbYqwNv3pQfO2toocQpa8ze=yG_<8nku5!v6QP2{T9`^21Ee{1R?ZbQr+fn zyu^$j(}NmX9rwCBJY4KPqN}tC$p<)obXo+74tecC7GpXH9WXVE^ppPvCSXLX*x+DO zFunwx%ik*GyJJaSTCGyugLxBz!4%_9?@zZd?Xrs5+Qb5x-xyTDOosl@K(x`TFB});CGg?(Y;V`r>9AOW;33X}0i}Gq9Wsd573G&38XK>Gk8b*HXejL79v1&k zP-uy`*6QT2#_L6rpFf(-6>L#vzg_mvKJqm6FJ=0fke!N-cW-|MbJ#M85pCC&-O_Ss zY7+hVXRKnd)BZ$fC-d%GQz5vx{61ObnPGb=-0fR58Lx| z4nLynl$pII5#%{bV|?Up)h}c36fsaJgFuoV^L-cIgHD{3qO( zYaC1kJlhKkw}Tx_$jQjSh(3q33n0rK;2hd1=B{kaOc9THKYwN~u~~7StI`oRoLpO@ z2XKX+sSXex*>?bKdo&yzrJ&^Wn#Cf=_XaF%fpS+m8oYV_2XDegsQlqLv(28CmRX~^ zWX@J+vs5ZD4@}QDFrNWw4y=iSoO5Ip?DI-fLt|r!qVRP>Omdka3%k=L@~AKP?5hCm z5$HNs34jyZ?QM(Od4A`9IsRY;i{8ekR~%Z&Qr{^O^{Lb1$^w)*bk&ctL3Blve0{R; z@sGT3f6mXVDUMH1&JOo;`p`O}z9p=9yE-yCX>d4iXLd`;n9y`5^tNiN?V*&#I?Pl3 zEyA8bUwC*EyU@iIpnvRpalV^r;5Hr0;HOh;k=QQ!owkl*{$mrU%b|Nn-v5G_1lr7yg>wUPO28fN__w7C6B84NAtRB{{Tdz?Vkf1@4^wk!cms)*ZQCT&UQTf!kVI6L=zyvr~?5Idm!ZwO4Dh#e^25|TIdvnW)1>`c3@MU z0hb^NI5ec-9L|J6MY|6;*w~jxb1#}I%I(gwa@(SCJM<}hKCeG;*GdK?Pf8yMfweS0 ze6Tw#tq<{2aRG(ro75N-@>lBzVE|62**>8`8+lCpCSNgW3a;%cFu#mE{%9Jt4 z5dlL5a%f^~D1)<~#V4-F>gq3_=_j?{2)%y&T3X4pB!?=4<IaWjCZiNt*`nUaVD)Ro3Hi@?%D2G_RR_3 zS6sf)r{6e_vGyf5A`zdpFjm$f-^irR{ynLBxs=ifc`#-k{B~*gc{z(G$3M1}NT&0LdF9aqQkkcU z!mk$=ur2&^NI#QNX(k$cFSKgtY|EkK5bl6_pgq7v!MEF+?Az{we6@6*tx%^FAIcwy zsG-yQ=WU6NMN#;B-Qt9i+fbFeU+F)XFQLrUa_-KuU`SpJtW&C`Wrucz@AWrk0w-I& zxDR6?Q8pt*7p}D!Z5H9x~xIO|N2?T8B!Qo%546OW!%`Rj3%yS_74!1ZTvcNw|2B*$$0U!WO zEKI`jEYwT~n_1sOGPsv{(wME!TWV^){jKp0M#PnplBuw<%?6=HC70%@nVGd3FPzbQ z;L8EzI~r1!-@p4B40lH-|IVphfa^4^W-D5`9f0j+NSIlPiijL9)Imc-6*hq=%sf-a z!zD=5b9tU3>wGL~Khj$UB-~<|R8D}neg_>w*k_jsDt|O<1p2Vc?3$7H6aNoR0f-}g z_R9jL;=3AxXuDyJ=sqBwnkOvVeTYCJD2Vpw&8$jN(s2CZ^fdf7G!*Qe zQu*xgM3s`ZX~5X;=t~50_78VsZ=s)_mn3j0p@TIDf~#HMaj-mBYSna4Ow@oxR*T8k zGvyM5g7;7(B^qBL`6N1_k~ut zu^GY>UD3(W$>+{Elo^NhBKVq*P&^qQfTeUHULmOOC$3d}- z)?LN9B<63BcZMk=x;VRigyQH4$SokELRaFUknhuPOIV5n^uW-<=Es?G6-0c-cjYxU zD~n(-tJUdge4W8DIrpgqVSlt(|dyz?v+#9ud{h{IuC)SNYm=o?FE{$e z$*EovlU}V;DK~n~bYSA(_D*mr%~*n>&57bc(AdZT>d~KM6E*IvbH++?-^_BIK;@0C z$8sM3%(Hfyn8S`H_!NL!Q|Rw^**h_OYSq#RjJZ?&p79+~ zcjc+vBzE8z&-hO;AM5Wy{*~XXfR@%_rIeY4SEbP9C1T)rTgiyB0jM58j#aNSkv2x6F*P-pY0;1fJf$Gf&TaLUb8B-o z-%7SsH$^)H(MkKL*l#Uf08Wa;f`H2S*8fbhLAu`Cvn!5D^A>#gtPNnh-5O?3?&r58PD+gik9l{JJ`(hKlhq<6rjXKvGGcW20FZ6v!Se zls8$dQB|D-&K_1=LTEhsy4_*Ts#iiLqxZv;iz4c_xCV&qXLY#e{>UlRUM=9BeOEbK zrhN1dl4~|jc%}WcRl~&ilSnXlxx+(Mk!85S6<7XrQbDorr&qN(_@=!t|E^zRG4ZDA zqthw5?$sKH*UZ@dAHD!g`u<~}@LI@yi(#j0g9&f-yvvIo(*#I9Vqy|0DJj4as&gEo z7efbg#09XSq$&}~1Q{KLnL3k;qDc0>arP6ooUQ~1{qi%^D^h2_9973Fm3u3V)w>~ef? z{!_YKr)NvPC}Jee%};FuI|Zx7<{I}ai>g2-W(B;Ad{UP zTb-^_Rr-zYmJ8QVc0W8s=E)z=L2p4!1;k6qoG(4H6%uK3tBRw=N`U(+8lM*i39ej= zbl0FpATT^7H4V^1ir3rf(XM1YwXA}p@EjmjN?9q{&&w<>$69tU)kOZ#FOBCe{7%VV zOPv7`^ZNR%A1khuQ?i|#NB-4lHeYf3IS(yDswxPy7V<{yXFAtlKHLz<0gOWQn*2pR zuU2Xpjo>EIaVE&NDfhDNub}I;yXv%BVV%*al)QhyRO(61=JWABZg-pf{UMvjW46k+ z%fsb_FaeAXmO*_13JLLmdXw`}Kq#tVp5eEzP}>c@T%e4DGRUkE-I@W%{+A!r8q&w$ z@8X^=saj;m2r~--e5F_*gG+IxpUric4n&sCK^YyMxQ&}TFjZ>3Yo5W%KXy_C_oppJ!`rrn@Vf>Z*-#mNB`SP}mE^!(q$_sTcqle2N@ znpivzS6GX6!tsk7$-fBST4)pl)=0-`W|>q`F1C}X;x9+KRnNuR+)VbROi>$QI1Tv6 znuSQ{Dv{rS44VA^5WF7^Etav$x*k&g028%8)e;+-n5wP0{Zz2;l1<9%{g`KO_SsYN zW$Tx%30aUSgp-4_3v02w5E7-L{cui)|EzkrXkyUtrF{`g4@297OqBaVxoWG2u~ zSw>Dy4g#ScZkU-B9^X7T2qt5b`MEb$v_T$+VidB$Jt&ve&Nt-BCQdb#}{3 zN2hSKA@c`)QxIX)v7*~p0J972c2@>2YCw_+1WOQLYJ5p@vH`~8uy@!HU}lp;&@TvZ zz5@2CJj1-+SAf3>G!B7Zb_tZ}kmI&Tp%?{x22=j@<$;klou~V9pBEnOSnu=R(D7jF6 zO-IuLarGaYYXU!DiV%U;_|RsGmZYRui}CD{WV*saQ^(`wg`O3!2|p;abvCp4ueje| z0O5Nqo2Oi}*rsrJIGl4VgM;>Zv`5jI&Uru<7Z^lcr(BZb_V#y-ls5_beXX_-vjW#P`dq4+vcIHU*DNmwhtI$;~ky{6DTQ;ll(>=xA zK)4-}S*0s8MID-dQjJkK2-Q;o{ER{!uqVz~49X+c*ih&xnu0N)`P7-AXk`W5uM`Hw zFeW*nfe5^ZrD1d!w!WSK8k3!h1qGDJIkvAvk6WE_HX7uSXH{91Bm|eP7b5L&xl(ik zzczHbHHNl^%fF-i6=OQsJJ4)D_*qblUA-575~8v7*m7}m!ev6$wJmQlaEq8@Tz+#y z4J7Dc)AhW*NfNN#mCX_h{?)yVTl~>tb2_0O)E|Aq**%1ol|?GJ*p^>WDv>L);!v8P z)vWL3Y%-s6UtM;yoO|}20bNbNfhPkXqF|NlXlC|zU2u5Srwej>1V)9Z{n1q_9U2H8 z9%#bmsUbD}vKbT@Q~|p4Nc;Xqp}l|befSeL+ps?ULDsvPOk@jjssqN&;8i<-(GWU;oQ@gYVrV~C$Mm zvz}{E&sV07i*C^Ke!E0P3CXqqjrI$EU|bz-FTf7AM7_dql)=v4QH0wMMGt7&pckIH zAaoi%0&kHh=4*wKp?^^}3Oq+4f6@O1vnzr**I(Y0(6fBzX7u|M@hMoM6x+OMO;;;3 z@bx@BngV;-cAI{!o#5J4kNM={#z+2|{z(xO{>+5z!0=~D{GND4j0lXk@Kt~cKn*EU z_4!bn7_?=Ev$ITyyj3gF`oeZH*##w9_W@U!DKVlYjI;9gmi;vgS|8MF7q7cfGb38d z?r{bzl&9Prl-8ZAlv5m~B5T5$dAr_u>v?h% zEz5MBqm-8PCUil3o!7RGj*N7HMLSGo#C-`?MYg!BU}S7&sFVxq)+L&>z$V+aTb3D< zR%jNqhvBUiV6mL^$Rm5d$NedjT(}Bj;R-trT^Wm)?0H`544etlGSZm`@`TqPla~TI z6pMcwW+y3|;>TO^Ie|EYZ4fsDtJ7nVne}jmD}2`1S%NN&-&SEXcw&e2Nd}z3ke8Du z&x}dci%p1^FifZ3cG1)RYc;|RhhA`uq`QsLQc(ViF~Do zKi30ja!HYiMmD;_4YFGD+8dsFmlrsU$w$OH_7yK{o5L{3Dv0>pJsU*2!icxQr`8_{ zLiKol*5;RB7w3^|w2_>F@L%q^O$0K11Ba@eEgc-hp#49>!~40m*6d_IGCU5GUUa93 zY-y$Glo#UFd!pFeCC}PZ-qi5(gDN0C8FA}1?YWX<~KXCDyDng`QK&peJ=6& zClntj*N_#y)Z8l|do{9kL75zqYGD<&dh*H;tw zbxvm5M$=6>bO>zThF|%2YWR%o8a9k|;C6lliSXp^?RR{b|9k=d3fuo@@#;UB;^)u* zrzHHp4}tMNCAeLmL76Bmtsz8ejiq0yLqTHho#qFztb4w>Lz#lZS@KA263^WC$G` zyf?BNT*?~#C$|i;iE6e`L_}PkxPG4{R zdz9dhH8aaoSC3PVy92rXzi*KOrf}=|&MXsG9mGra+}1$&C3ZW`w1<;hJE(%FH~!%Y^bnVqGd~P^|5aMVK&Y@d**iMA`dOqH>xTy!H_{uzBa%biV3(-%?}0s| z=jYd8h+%0FCqNSWP573T06h2=H+Ns(s*60f1cUsq*H6-T^A+8eBjXsqBXX| zqh99})Ju-pdUr~oKBBS!1_5jl2Rn;Miwige1c}kvyxdS)1|My$&C68@nu?0)oJkQ8 z-e_Ohkj6=vnH3=fHv-KqZr|6|cIjD={co7Oyja0Rv)MiVvdlQ}E~Iq;Lh(3^O^WY< zkiWnC1Yb9~Wnf@ptV0A&mGrac4CtN$=(C$#t|QZ}MiaXU@4U>Lwg#&p zSyiVE>`+QM!g*KIf#@a6*Mb;FM9rd2ALEjul8c|cjEs-Rj)_f-{J?Mc90%Wm#mpP^ zl5_7XqA1F@OiM{Z7?Bd@_4DV26~*24oiW5z+`A;^pa+FoxRX_Z>DZHpvfNq6GLJ$yRv@pG@W4`(A-&tkChs zD@=C{thN&Q&c3z0TOZ3e>tUTJ)JgjA;cbn8e#Z9T0$x!Y0xe*&t^qR8M44$5z?fTs)r74Odig(e_ zAiVxI2fE!9e*}Q*Li>v<{ z?0;1XUz7o+3|_OUih{%1IUrZrZtbZEdkSl7Cy@d&kX}*Q?GUz(GV$zDy-{AHhg)+> zd5RAs!@)gV__YuW#A6dy{ovc9kfqXT__VFLASN4@Ea0w@`m9BWogIDyj&jH%piya= zhcO>K2!6;2mA1*z@$tzK-YH+Gscp;3*a!(SmlNXS-4N4JfL>z-Z`q6OrPd>)Cr0Nd zRrlBkZ;+VS0Hg4}qw?i7r1l(JRi~eWg1p@8&`hdS`JxQ?lo%saW^T9KI0Ini+tS~) zM%TZ5Hab;ou2QRo>V68nkZWdUC`?KtidSxDSxp~f6ug~KXf%uCH_VRKdc85;x3lv! z5f#iV_wKRn+I>Ad$g_77b%Vxa`=ZXQU>sXg&^*mx{)LB!2W<${d1jpEkorxy?8(<|s*RU|9qp#hpQEzNH5gd1EixFf_}BwjvGH@l$0Q!R-o^!?|nz5Dkd>eIW<)V9AE9O z;RxM^R|>+y;^NYqvstLuC2ed<=tN#>xZ$xoK~;((E($6tpXc89gE*EHPJxaG@>1Yz zEmDqShV*eDODeW-xR0Bnddl)(em*;a{Ah~pt}S%1jSnjljl}+p&Hb4xJvMEm$A891 z_=FsZ?YTlr%#+zl9GbhIu@fPeIG9F$ER+;#({vpNgG8llp_&q|Ihh+wHtT3LJ2wsXjHZ`rvN)o_WMsd-?V#e zV`D`lH6{k9oCs41la{vNVRO;*5hhYngN~Bk9GR3f{)7?39Pm^=!p9}%ajJy6&9VHc ztn5q>tL223J%#56KkI>lQZP}7%k6N?*Hs@&Ldx4iS*}G zgB5BvH~5x2L-r4LW*beM8|x75uCsG<_lfyO8`SqfnVVLrk`kBv%&Z`faB!@OY`Rcu zYdh})_9FmpkC@aYnlo=B#WFjNl=X^D*@zMx}z<0$?`Zfx+- z`|!N6-Z{b{ci^2CBM&X=K2?TK%{){?V<<~NI9ml>RJ<4B@(eFh-!yr9@6-VBh5rdx zt;)vuFd1xDpxhD{*WHbR@`*kNX`;w4dC(9ca38oo+i1^2O^t!O0n?fAG18mDd!-(0 zik2>OZiq}Mzxvph#9{*0AlsfuDs@s?7EE9K`x=@!;q(1-=V;~hiM^bj{mER0R#sa- zmNe>R32hi{=&GUoMpU#XiOZ#X5L8*%0R-+Yoq||9KBhp?4+rlNk97k~cZ1qnkdW=H zpca=ym)DEmY|m(OCo}Uq6kjXv0^9?u3Hmq~)~%%V>_G|NKPtmN_VZKOHi9F};+ zm->@<7Jm5?1M=?y=Et6B+F%+|aM|}n^>U(RiI6M{llwGF9&OKwTsZz77HM~b7Ajq2u$TrVK10QSUvxzjPx6NX6=lG4d+?ww#h za&)N7I=JJQl(FoY=!W`Zpo{C_qi-eM@{ik23=!+E&rgG|ag5{I8qL)qzH8VS%aEwO zW+ET?{JRKjAx~gv&~ZTU`?P3=!#JNA1CuahJqsGhbBTAN-luMkfI;mVww;rQws*<3 z@~DUn{&v@u-Y8)!t!gb-rmPP?l>Kb&SKkwGmA_gf1>s+_-kMNVMoJ1lN3l3@sHF73%Zhk4N$&gTVv-Gc!z_~8#rrmoQtYxdK(bN->BzNoVR=N0jdzC#)ZwQj{ z@X!ixapz4Qw`Vv07%O9}cOLW6V&q^Rtgmx!N-pkq?25S^NDYQCR*#Ux_~h8g58;vA z>2e!yRkLIJ#zroyKPFU1f1RG4>|^;n$3w+L?YQqv3)fu1G=&jA=PT&bi2=sIlnNb* ztfBYhWciajfyBRBIsji-V}tO)u!kxwoxw?Qo1PDI*GM7~4t1GFC(INUWRHnbtyo-k zW3y7DD3f`brw z(DuVjR>m{=Ox=@Z7we+Y&Q`%`M9HzO*7Fp*6=m~i^3x~qLgx>>ciQV& zP))5&h>Ke^0VB5C!RA98{2JH&Y%O@VC4Qy5859>2(-o1?)&AGU_gzdXZ-edt`E6(c z5edRZyNlA^zb2sfx$8W$X&t5pWMUx?bOXL0b@awq7c=DLnfZy{dO=Hoh4tZkq|sUA zb@?vjP%q$}Lg^Yz{gxLOy)lvHZr-?YKsZQTcCRP@Bzb6DF`Ano0`HLRDSmoLWu-E~ zEWmemH;d&3r?kb@)b5kSG$l9HX5DNphjCL{rQx>3!nMbfkjNXVDww#1MnuQ|;M=zI z_I#sS$$gs7PyT2DIy6!lTE#g9YIVxgR)kj1Y1rA>i8uEnBNI4^6Jp~pvS;@dUsBw%w#Ky)RP7gO(3Pa^%x>^2acc4`#fvCF;Lb4f{mq4m3^6&M4yqce(#iC|j}*%;S0Jj$ z`&Zo}ezAK5_2^o;Z@BPQkq2fs7OEuvlhz+aqLa`wb;z<4d1`NGj(44+;pxQP|3*YV zUp-5@c*1(td2Q4<2ZE(wJL+tw{EaNeYwrnSn{t^n2AYS{K#!1<-(RRHu@k=<vmxiOwkL#uWk2W?Lmo3Znn5b3+w)%L)TQ*m+Y zd_Q*Bnu?RHVg0kTyaWM#Uu+E*mm3D5Jt{OA-X^IwRJnBmU{&L0A|c@=^%!cs36+@Y zb(T|SuJ$8}Bb{L>FS4cMY^IQ$trzaxyQfNIsxl!!Zog)>W@<-C{scV2Y7OJJL)zeo z(rcBXXXWN65wiDfRcS&86YZqxY=f;~^Xqern1BsTOnkDBWo4X*O92BB>hDhuBkp{=S38IXor zdcVCHL2CM?a>t4DE|s^$moHF7K|#@p@}aaI=0v|oL6_&v^tv{k#bjCNqUu(Bg?wuAfiE~#B*Lcm|UG98|8Ihcbm%s1|-Da+#4B`ry zm`a%+!DuO>U^7#kw4?S|>^(@Ezl+H}z^XXS+qC3QgTTL0Y4`cz74 z&>@fGFRgrKvvvY;5n_SYZ&5yXbgUHB)NIh=+?N{w!|m<-G~ZSsVGLc;=BWw`wU??C z!(&iSP(^x=GITkdL|6rX@7oEYz_>S3I4OWHF+Ljb`Am^=1Wg|)Lo;qjdZRAI);n8E z73jaTR!BF>3+SdA{R^=%!Q*vkSsf0jpS`7yUUuSN{uUD@P|u#GoTnH|#ASCpX}df+ z63tjqUYW00bw?Ik3G>R?4;wgX2DO*2fY+TQ^(h)oq~h`o#uAY{{XA3#xWM`3#fwE`h zj^)O0vbC!^%9t zWo-(N$w=h zbFy;B zMSBEt?9^dqPPol;akf7YFt-aZxW&m_S?xOM1a4d=TVO7s;~M5Xc|tLbRSt54DLw!K2%EC=p_YtPF%vH`~mEk@Y(e3v&m0JzWHP-aTHv92C~+1I(>)lg2m3>wZg*f z;MjMSaU<-d@nRcmQ{p(?->Ls2U6c&!y_v6am8@>gmM>~8%N8p*aA@?HTupO6hv&X* zTn)h;?C+c|w3NW7-A}YhsHlj-VSN8<;pOP807m-ShFOOGyReDL=;eg$qy?@5qllxJ zF_+)IORMcIgdaXAAv;*SMW_SM3TEvH^B*1p@9Fb3%05MF9j>+%@NtiN9nZFnw#ze& zJQ}Z$k``tm;i|H<#n;x}rQh3rL)eWnUZROZL=>8BNAEw)Jm!oLxBGxTPfq?5|2ylw zp?uMgU!nE_wuw`Hpl0Q(l!Qi?YDx7@4@-KUk$q0@3{ku122PtK=9M1R%AywyE`ebS zcxDhIc|9~fKK02~xGx?+Mq@&Eo*Ep#GP}h(1Q%hu)x)G7m4fz;PT2NJXV-mN0k`%O zsmRCzhE!oCFA#>pk`louQtmKiN6;q~!+@9Y2$|x>>(em_NXw0JJZ(R;UTCuF8O;+n zGP1)>$E|cLLjA6#66AQgRkHw^!ofiv%k@Hm8mp@E%I9z2yd<9<2Ho=Esqk2l8gWE> zBabtE5#4-)tCgXlQn~VNoS2wyi858wh#)JM@9)H`!sFR2CsAtkfqc0Q)FT_y!BRo8 zch1XIS3H8PSF4nA<}_kritN@HxRM)%BpxH2PYt`?t2&(w4g#LVZGH27X`iHfv1t2!*#QjfB3*`Hf$sq)x^cc*P0d<7%158=P0V`o8&i_ z+Y}N-qs9QmccFNpLzQbRJu!?|=`d53E&>rhp`#2bEx?YWH_(U?R8&4YBhfi~09$*F>$**acT3T^`r^4Vg#AM~Y~};x z0=6g9KE|4ZQ*?cPjt6Bgyx<-&lj7z%)+6*cNfcYS78YPN)A*(mAQi4h#92!0YG81E zxQq+*+GaX+8VXdZmw=7z418no<}YjLNUyFQQ_zZVNjI%Do!8RRfY6vNGwsB>Kk+z# zarah&Wq=v0RYk+itw9hhbFkx0Ce#TNcQ;ozl2&=uztq)V+jyGv#Tn`wQ)j8kXNqHZ zUH(+_zSBR4ijcTW;ud5Rk6RB^uJwkXE=+x*< zET+W7Oh9WktkHvbZw5L@OVb^`a~t5aBs@gwzquKHqw6%*WdDZ@imTEG*dHX5YJ1{h z623!Qn`mx!kw{wS){ArDDjof;Af}1D${EBRdhd>F$$X>j3)`1V_tElI^9C>sZ0Q6k zVnFD%?JiBzu>0MKhVOM!=D$bx8Yd(jwm7j)8{;y%3#E2X$ZOdlwds53p{PEXyTUr_ z1xRzf

0Jp*JE+DyH++e3Lys!fB1!xGYpbVF+rgK2O{=c;hQ#D1~r9xFOcGfi?$t z)xe}VFJ31zcG)>yO z6<&Y?Q0&${JP*A~{!B+sxm~rkjun-$d?%Fx<*kVlV)F-rxVv4D&-e~9(WDyWFOGG{ zSy}H^A+Hl-MZrt~fzOJijG$lQ5`2iMTkG~P>aSMGlp21-I@`~=oG^!Lj4(-EA^98R zvc7IV@VjqdE`CXgncq{jf?nbCaAmRQ?>kunKVKUgWO_MnpG7K*43yD|AuMJZt>$n_ zJnKWW6f(ad>o(_PX8VeDER(&)!0&ZL^Y#)j&om|IZFQ7#)-FY?BK|l`AnwUyZDF9%;nG-_!_UG z9PnJbc*21>HUYkIyF2qmC(nB8G7zb4kcT5L@d_9KgLao#q?=i%mNx0oO8 z?LA$5mspPsEE_xDhnVV#FCbllxNGZOO@dPEw9oST~4nk znOgvZN1~;4ku1{!f2jM4Ksv1fMI!)sRVz@ns!ZbL;~hxm3i*~H*ygvC`czl`lf3?! zFb|r-+azta!n^TZD`UmbP~*&=2xe4>kA}Pt+1%RV*v?0J~b7WztLS@UQymm($X5#m&`KJ({o2b&_EGr zzpI`NjTk|FiEL8w$hU8nSHC_#$v_;#)ET-}g?u3+5!E(HwlOp`7IC=$A*(lxfIkxt zZ$YT)l}zI4izmP9`#3l_#9m~Hyi2v`7?I9 z%0j!|2~Fzt#pd6eZDk}DdL_pbrN#{XZGKOMdzv+7@4~U-CgMhNG^?!7JJ0)wEygRY z=QIdt@HPGC>fI1Dkluwc^}|wz$Dn#2^$Lfk7JPJXsF`W>{-Xsn_@Uv8iik?bv%;ks zTdF{Z&xFoCu~ytEnqC|Fn2g)8-|orY3lz7{G|74r@DbAPb1F!=!IresM0|G7U8Q{P zf~UmCKW8;n@EwdHL2U4CE6OPd)~>OhyG3vH8y2}3J%KkLA&cnh>Utu0d3m+8w8HP{ z6%1L6i3V+PJ0H^ZM7T9S`i|V_N172q%7?q@;P5w==Q4z}&N z3I^@(d94S(we(2w9R1pw&I)#M#J^HYRxacAE3UR5gqnW$aJ8O^c+P>}H=J94LDC(G zlBasN58rv$my;u69h>Tf-q^c?Gfk)nQ@K1C-5)XE|sZgVD<_9u>-^1QOf+OQi5|1;m z*Iis(qUqH@cfYxc)BowN@6v>E@2Guo_@JHxfhk-m*Ngkc0lK8Y^?By=z z>P|qwPU%xi&enKW`&9?PVc2C!=4xcUe^f6L_10{CeLVmteJMOHHeNgn^_2%KAi}h$ zSXq_jbek>L%I)Y^!c}swNz2IiI3Ia=oE%Wgz5uq2qH>HonX@oPtMjefyI?M+`L#=^h8cd?HE~1mT){z?DS+fF}o9VH1!sJrta#U zG7dVLd$u34ej^K4EUO&5{ywZQTqR`MWq;ZLq<&F9x1}-SV^@bZ<=uRL@TF>N)BhIJ zee7)d>#6G(hUV0O;F^@APY1aYTaxD#MS?+*k%-%0IyEN;YTwX z#x9{z#BMiRvW;+0jN<%|*-&P(w%>V>+!nND%i;~MEo+m6X3_spb$?CVy!;a}Pl!*% z?JTEx5mVCIjcJN&mK*=#IOud`ABUu+#n#pqChyOK#Tyg+%=fMi?Dhsu;)ge7$Oe$(7 zrsa&8&y?it3k|l7PDyn@XcBBlZ-`U%Bc&(n8|!#7J596t+9OA9B3EI5*DwbvkJ7dd zru*2KNezAurkSnby5vJjqqRn`;M0e7euK%cVLW|}ulG*|7lmPxWqJZ5LPiSiJ4HOS zw7nIp{pUNs%&7~v&RqBV`ugbT>4u~mUAImKki0~1yqWA69?o}&8-~dl#!Tf4{&)*; z?@Y1)Rzf&~iHYf2pr3Y?IRwSa3l1-eL;pi6xObTU52&E0u$=PdVZ#pMFVq>*Xt@T$ z3pTT9hmMkFQr+AgeO?ni(>I&9`u2b)z{BS@nq$-4-QBImsGF;c4kP(IA$DJM%JZ}R zXPYxneb*%$21v1-l9C*-uM?T4J~#Ukb!07^Q3=2T_U~ZxwZadWo0VMV#25W27Rq+n`j8URi?9~)NIk@ zk~tNcb&&4Mg~7l_Hi110f@nfc{ezpsp9~*tHWGWS59K>1oE~jkO@B%QP*k_)4Hf4o zU2J;E0dZkEL?8prx^0>{9uGEC%E6;-+gDjhU}hCR3wm_$1*)Bi_)I5?`Qm>`y;j$P zjeIK>wG*{YA!kpsOAU`8?mRuD{RGXfRPG+6dtFe~0Nt2ZO}e_#bbXFSSB>@sP+)HQ zeYOtKiP#VeBaGuVj(0F@0%9|-*WukP41Y#pS8b>Dh&F#r%%#iO!&SgCVXR_w?T>Se zilx30T1=8RVvf*yd_pzGJK!_Jz9FQ6UScyh8m6Ec_qkHU8yw#FSVn>?vxL4g3o zS7D6H!%2k2M5Hsj^85~i6j!Xal-Y1l_+qG%0-CG93 z!-(ga9Puejm}?`x%r|*jT-nFYTu5>rn}X%ONvW$pcB^PW^F|&3xL;JhT5R~e zZ<{X}+^SmO*B_G>u2t`*66KqgXz|jx>3*raO7-@jK6np+mLxd4EngqqoI-q$MY0|Y zLp|>}HPKG-av6yDU0harR)QW{8K!4=dM=RuYJh;ok}kFvXX>L1a_X9&-!72E!U#IH zljhGqrg;2o?Ol8dY(|ZMJ2%ScF1Q0~ zoRZkB>P|wi&|Ann+sq~*1x`)h&&73`APYOc5$d$!MYX~l_inVA0lU&%)XQJ z(gulabd}d>(L16^=Nc`Gr}m0x5>}l0?(Hv_INRb%npTBdENUq)5s|-9G;Hg*HJEGz z$7rKYjAC#+x^tDN?)_R) zv>U(oaSqn!G#m5YV2i`#dT0{ctE#-Z^)TCn%FZe?E-tSs3enW)-V5_d81I3(E+;E% z3IDjV(t$AP$dCm2){XIECH%e#NSH@csoY_D4&I_V=k1Hd7s9RmN%i^DQ+$He2S10v zk8_WR_tj}XJuBTZ=8kSds-?oIzpwA%#sV=tZe<%AD;s)F`pw*;jQzb8)OO*J1XgUE zmG9quC^lA|u4|}Il;{c*kmXvxrbJ`(H4-Fvf~PJK{DYoGEpKn8X91YN2g_l6tdSWg zDtGSPDO6+3akVru;x+v7mC3N{WkI|StZ_kUwtqPwE2DInKUrto3toqIF0{LMVHyLq zmRgmjWt9E>rsFjAE=ES_T=3CFG5|#H?G&Hp9oGOZx^IpmzvtG|C z2x8Fr^sR&)tFq2gu$JDGWue(wO8nVJ6<4^`Q)ZnRpr=uROd- z;z~Qv&z+8bX|qz4sd^QvgVnvMJGZ+xh7sIezd-a$PEOulV5P<9@tP=}<6JWJG30_w zW?+De7al1;twy0swt08OxA4ct)9GnLg|5NC?yNpfONtD;aqHIO2EYXh;uA@rmB)6< z#8WMcpvm|McB5$NG(L!Q)^Ir5J6sY>n`P&dk<2?UWd_n~R!!Bp+V}eznKW-4zqR|_WRv=cFq%q1QR`YnLV^@T zjoYIp?LiEtl#>&~zR0NIZ zjE_VVw^lcTpP6)iRY!4+P>`%}b#*h~v$CQkYp2sF1n{G;jEuBxfc;kGn>T+UXzkq* zYavzix(t*&&oIWv#wyCI=Yav)+;nc&I1WyX*Q$92Y9nbBvT;EVY24ggatGdcprw6> zw1Zt^>D+yR!r#mH5i^T<3w=uRQJA)*baa*jzqCP2InNWjuPo^9wEcQxi_dHii#od1 zyI3iQ2;29&c;RfMp;=zC$l@176Tgk8C9T@<_;9g$#SduiR8U7F;%)CQj<20MZK~rO z1okA#L6-iIt!Rm6GYK`d`gyu|`_=N)3IGR)j-UC?+x+<#nZyliZOEZOacudGiT?9) z^!M$zAho;}=ZzSEY8L54G2)Jn&*=DyBQ1-o-bfLFWZqjt6_nT4wVcJ3quVzvGt0aR zHIGj7RgV4L_<4Awgz-eSg>$*GuGN-`Eb(DB5ni`g&FP1_%UuM*^I(T0A8+L^uP7Qa z!hT2mLFis|b8}03DVzp%=V}YD&*2<4H`ROU+)v=)OuF?#Q2k?rz~JQigAS=BM(>k! z*<8t9fDg{jYF$~tPi!Cz;3-!~FMY}D&^Jc*>N5VeCq-eNV)3IUHLtxMTCOKWr82TL zNuU~FtibSv-)qYvwZ#220##$}D5XJgy-a^QYw}Yje-eHaRXRb2CzyB}x!K!I#$g$1 z#phx*vllUr-FbXGlYhDNF(Kz6ePqQmZNe?sZw3)&kh>rke(6IldE1u0TEeARo{I>I zLn6qJW>*U7WGk23&?+KqPJJcU{4K(B`lqQyAdb+n|MO7YQf;ZN`Xd%KVp^=Yx#>}$ z2D6y|8jnWXl6*+}$!;UBUe@}fzeAzgvgdYGcdmm*jDvvq$a99r@ zU>RIl44KQ~Vg@A&IZ0VrB4!7w=^6hp0vhGq9d9ILkf_M3KS8q!hKv4sRYF2iQc|*A zK$hK-{1g!Um{gxAQH6_R67hQ|r#}zG8PJ^s^WwpA#H23LT2x2<;!ayzTaD#Bulu3p zW}ExbHX0%O8E{p$EGC^dCnc&%uoUmp%}7>*ONbb}Q4Va0C`E#-nX7$qBWX%GCun%t zndFSnx~rezr&g>*t=e!S^b_X{tdX>IYqPU+pp5|}S?dYeDeZfLMrlICv*P#MIqB(T z)YMkssdzp(kD40nEKz1=^g5M4SAs*m?%Mp(0sxkhq*LXFfO#qu z2y?wYyMZVsn7og|vBJ!C$eZ2B*Vq~oGEXV`_S;_16HdVbi-No~l1o!g^RZ_df-qO@Xt8rU z1CDw+=coK`2~2!rrQE0Yb^$tx-ke|Z)jtNECa2zMqi$(JH=;v1_r=i63^ZkHwk?FX zhT(vFzQnZv8zgQY? z;E<5eAfYp0`}if55M5iQ&!3rirrwb}1eB(M75Uz4Lw_X;rP0Wj0)6@>qjp2UBnLmq z;(8Trn;)7mw&mWh2Srb$V)E{10PcJL2Vw)D$25$XAN5_!eLfw7`3s@YoA%0Rd$yME z`4&UmlGQUn4|iz4W4Z25LCti#;QXY|;<@&^v9Oq0jmz$X;U{{rQ?r|ZlXbjWSdfus z)H!YxUwkT~!53P2jP3H=(q^HF{+uDREzb=o@-PTBK>W+z7?q_e$fW`Y+)SR534)f@ zHnroa2t`a_n2E8;7h{ddMb6-{cLIsa{QUgT@YHO7%AOwWBzt^Wu~$bF?z zZSJ_TXAGnZ*(7d~bnWI}VYT}|n!WU2`b`?kO?639i0ulK4E^4*4xF4kM?R!IbzRYN z4cRs>2j{t+c6oj^GcI#q$-?A;Jwu|v_TpOL`@0bqGfq}zn6DZ#LmIs_>UjA zWka`irCD&dCJQeeffw~vdg$c-m8#I`J>tC0=Vb9suiisL;D++s@;*k68fLd2>nxQf z!4y^Y{9;7B_D3Pk;nC3q1LL`h>&G4bOTG*psOntJ2a*y}UO(1DW>@>kYSM4g-@2IG z?0lz|@xxN^)C__PkYzZGl6+WI7F%XO%*<@-?Xuqf!>m^ImJEd=3@Bw2!ypJ=DKMQ_%j znX2kxb=w5C?0S{{K-OtLV1EclmDzr&jO}%Feq9KK&#d1Dn8?nis%ldLs8a;P1sIK* zfgCRy>{fqwXxk_1xD8xbc_H}+RfszFvQ?_Se%ENCR^&FI(+D~o0U;rLB$YFGS^*Np z^3K$^f(w|1rl|yCE1Q}=!W-EC!T9RP;mT?szYvWb4L(75V{*HFe|q?;+)ZLAqag9}W(_)m%UTFuDH$r0Q5Tp#@;vD}?2UKw*`CFrD}G#XNxdw+LL<<+YlZ_iUW>CQ8z zkDtF?$E*9IltZUeSt=D%yly2AokBkdVN5Sj=5f zzWTG{APS@Mr>F7npiNI)T6}Xb&2XhB%0xN^8xxbyZq&5$F2)0GPUm@OE?g~=3rpq` zek(kyrTtpFS~?_Iy+Uz$6u=c=q7xMbSS(+ zqf#mdbU$BF#QdClJiA#?qzSAyH92fBxSiaNf2O6B*i>Y5B{7GiJ{GKhb)i-KSMSKk z$meHXt(g+t4zRocGtj~nkB|(X&HSu)Nn{|In@saLyKt{>rOEmL}=p!hb;GucA&5-?$ z-D*Y>l-{){^L|UKDnD+?nAo3z?Qf4(Xp_yy@_jcQfzQ3Pv=p&-mw;1VUIlfDk5qvj z23igJ=XJ2d+F1XImw;DruQA!dHrgBGFwU+Yw!z@_eGN#47xpYH`*2wuQ{I^Ndtvf> zIE(55Fvcs(6*~`}0N?zRK9XX`-byc<*|2iF{jGlPlJ}d*j3k%Wz3{3ePTRASkPY>G zs+-r;1rzR*zD_)qNnq<88HNK3${&m>&-`-(lC4kc1NV-y?KkX_Q3q>8CLpc-jmkH zI~eQRE?}Dn5LPOuenw&<(V_#kO~RYP=boeV+I({wu-YwF#=ZS`ok=i?PPg9eVXb~d zt4QWlX@PAhDzQ%b&x;>uA2`=2keq+b60$o`kt&n-g8vT}>iC+P(mboyO$L%AWTizh zxb1&bDvuT@7n4p5t8UrL@L@6Yos+crdAgjZzNr#=`;NaGD`yVz1wcAD*o=rw0 zsTF>Gf9rg7c2=3o!%^JjYh+e^Z*-~p(?$0ay$}L`qFnI^Xn-}#JKWv=36{@_sgEUO zBy63J0s`I#C~5~985r#?%L(K!4CpvOav@)(fYkL1f)lSXidN-=A5X-$1XtFY(PnQP zk}V;8w5?&jdgm})4p~dwpk-32eE_N6LnB|M?1y$?Hgj%|hJI33#<7|3Z|h5Mp9$eb zsb}Kp@uQ2WX{w2N)c@rrkkciXuUt%bui9?;1QCCat)q#g$z~y$-#%Mp|A&F~-qLT> zIIRlhFN)%SK`9CP5)=-WnDd((6!AB?Ad7VO_Kpk;2+*;T)3MTVv){Unn$Bb^OGly< z;mclqA_XgT5K!jVernFsENR-oMcMu;)5p*Cz3+z%zCRlAE)=jV zh`$%}y;N5=8U-=qJ`wlXmk2-p+wMOiMluB7MGEtXh=^=WRph`06Gg_HYxF~C9@IW; zsFhTkkBM%!`)FkdpN0n1!5Y>7_|~>B*#oRcJ~?+o?n)InQha!^S)H!2?W- z@j?<~rWc{hji<0H9+#gpkVA^|_q(-(zjh0SN}Tq!Hsm6(5ITBq^Jhkf$iz+nPMTij zx=+y_VQ?$t2#8*HB+G%a;(mBnf#R)#iY~{>@6;?(`t{>oLll1bMURFn1`Tj2cuc=N z-o0=`nb4Y>ZNI0EVk4j4zVdAsr0KQwb;FVO3)&<8%7k$|vT?S{U8^+VZ`V`F-ioy* zZ`fk#*{*&oTCtNMBQsn~fNmh|W~WBgqUm9?jm@)6`LtHWX$GN2Zy8Zm#+M$zXINiu zUu?gRg7Q8P*4pgYm?1PZ7B#=RHSHTGE<8`sBfUJ9P`5AGZ!g?Ucz*^4KZ@4NTOH3f zWdrcmK(B~Uz@U63UQqkcY{y66 zfPm*i3M8VP4Z^hN6F?v!I>x^?-i5vpNu%*BH7qmB#4zi(zl65XyI$5Cztp<+J7(!3Qe7V{V%Mo??J-D!-KT_f6CeiBZdDa8NfQ2 zHMq^i zzx?X=!|C!TWdFbT82rEXA@=ZBvi@iR{~RuGr2qbJfCm1C3&8jOef#gzI{y5`|F0gR z_Sr$ojO*t3v^LJi4n3mV*xv4p;lyt6NW(%WabL3G_#fmI}h%$lTSXCT{;8wa802`~RHXD;AsH|6f0v zp2!Ui4N$oB?`xBO`4Nf+F3oUpaVce!0nmtX!Jw<#99Qv!|Me!_*?n}`tjtW0UsHpF zrP%Z{&^%-b(hR`A|NW#Gx@3aN#LLQk90u7$&LY9Km%Y&?XYNd@P&Dt2bMny$!sNcUo09+9`muE?-9xZ-Y9k}vVE~A7aj~^nSn{#!H+lC{?(VgVn9Q($PKBQ~|C|X;n%!FH zpJHPl^%^lRmLxpYUx%G=$J#B##;=8D&EsGISL1P*QOkl-vouhIFOW=0Prucb;uZrb zeAy%o0L4>7xn?o$(lTZ|y9FUGZs1zV>-EE#AmjA`5cI7P@s3NC5fO`XJRD!YO8a_s zL$sF1Y5gLEB>|*PF3$$jA<{pujh8qB`U*Zkue!og-iL%p4T#H0^~zIuWjCkDc0Jc{ z(-3tN)v&XJHev%058h$ghQZID^?IYx(C|l3rEFr7l01**J=Z4EJA$&8=h|zOsJxc8 zN+l&Ez5ts810&*^cMD>D+y@Cg3o<-_@SBGMFZERlp7q9m5mJxL&J=m97_K5 zyx%x=`l4c8oZS4IL(7^-jQ_lu(!FlLg_{5ES+45VEiao`O3j%d9LB^8DMnk5om91w zYfF{U(W(G2&}s5Oti(LR#hq_>ao%FLQV>8^I5VThoUY!d*=D}J!_G0mvxCwZ9p?PTMJ(E856B8FOO8Jua_ji0`j<_|0VJZ=F0)~$7hZa5nfzDFas%J zohw+CyB#Q@i&h$b0p=xc&QWc6a(=v_ftDJ29Yq`Nz%M7p~}kS^&i>24|M?vi|I zkd*H3?yfr?&-vc?2k!p(i#qS#GqY!}c-C6a(oN;}RCDX>>)V~$fFLXpl5%eB*T1;F zs>f;X=m6}5;NTB}9=XKX(fCJGbyN2@c6@0Ny{yg0N2tWeUxt{}@!0>0zDEtfndG@M zSLc|@Rim{qFRu`*<=aPdy{|ed>&u@THi2HM2Y&ZXkPOWo48>Q3kR9QrgJ=DGH&1!{U z#`gS1-c;EV1;NqWR)Wr^r@h>$?czn}EvPo~0G{=|ZvZNz1;Elvjiclb<|~|kFUx@r zq8L3%r|0J$Ylq%JlriLNrm6(5Gcxp?xDEAmoUkvR6$#xr0N%&R(dhRVLTHW;-BQL| z#!gL!zh3EMImhKi4?iTlHW4X*QaVBS7q`v(O4>7|uYTV4K!*~O28HlZI> z@-H^oV0$7Xq1;~Ae2PVTuT(6n-C!$=RNjI_KT~0F7K&pBN)6ypaVhwmu8%A3uJ8c+ zWKjw2aBr{qNRx?Cy#B_K8AhjDv zi*Q}w*cK|})t;N_%9g*;O-fD_heT0Nz9Zw}%t(_<#RVs$rmhZh!Ws<%2s$~XLXmS+kO3h>sVOEcZIWgwY0NK{ID;NDLUVa( zGgFXwXft=IwVDz~rG&vJLpS^@&sJ+O@v?L&DR=*&P(Q2l@eBASIwABlSbKz^1n1B7 zu=R&bPE0~#uEtD8P+$M!N26RKp$u6H?gt9j2SdZ`qQ`z&Le(~_L}pe@{?U`CYkE#n z*qw<4?8oa0J5r4~z~i{-!UKZbPu?A?ER}fxFb>6K6?DP+mB8_Z>t}tl<>Kl?f0+)X z!tx=vM1!q&gVQ)nFk|4`_-i7NdPdFTI^-i$s$pojzR-9DXb?0kbPHx{Oxdg!6|X}< ze#IzwfjP&{qvC~IW`tTC1s)v&78bNSC-h^#fy?vXn*!nhN0_JI^WkAm^utpTsP8_r z@v8B7Fe1?#Ei-$Z{@v(EZ}PG@05!FxycL)t+>VEDo8J;?y+hlzgNjc|GI?;ZKjP-Q zUe7eQu*fk1ocShPfMe{sDfIm6xq8x@WH&wOnMSh*GMS{(oSZOKG<3{YAF{ny*LK|t zzS@5U-!sEVp;Cx69(Ql2rH(k$1nSK8$QrN#U@lw#)p|VbfRQXTD99!Pe*qPrhv=T$ zuyS4~8Xg7)pC{X`5wvq}cG>tI8|%l=KRi4?YK5v?q5x=?xS)O6B7Gii(PFl|pfqPq6_32|p*MEk*}GG0&=BM$`&wpS`3#K8xVW(? zN?u~)>%8ai;YNjrGgA;9D}YdfR(Cnta>#GlWOaUMO3c{oyns@sOEP7LVPSM$@)TFO za`&>|3O8_a@&k=l6vQ}55(T4jQhM7BbH4@%@3Wh?V=qcHOj@n@)Pp!m6#mhpL0L~k z+;fuxcvUem&ga26Z*kmhO-5Vv;s5of|1ceH-+48`L1B?W-#_<khDbImLEo>qp&}E78aKK`6_v@i+Z^t!y~%)k`fXz(X3g+Fsh?3|6`Y=kg@(`)E5CD({}`J z4EOaIoxL@Sd8OLgi;2iHF~`Ut^NA!!#b?%fN8ii-Vi)ucvKHXXtRJe}pssza=Y9Vk z9m2)A@p3P`w?NtgUXrBcEvkfsM3djKJI%!gzUrupK3?ZZRklijvFZ}^-PTAJeHc4^ zNl@=&=8BFs64T%?AJJ!RqiQFm8OEc zUVfKzm|T!5(7c`i3!2jeGP77MN=l|iFKJolmBcl|PbQqVRGixzb7JbA3v8mw2rZ@R)j?5=?Li;M(K_m*@drfa2!R6$@5G`xzx6m@)mze?&ibF>-TZwIdmd8lP!Wo>q` z=WaBuIR^X>;}?5%blMzYJ0M;{)G96^;ktfqIFypqu0s;e^29jkXrK!V3onLhWN3(> z)A=9Lb_xR+Ohp0zGLPvvM<;tC==6AB*JL=5h~D=8Js|4utgfq*sXJ^2y@Herq7PJb z^7-H*-_O@5Wcrcu@13NKmk+)q6}lo$9tHDxuPpTR+U(K~E(&;f2Pt)v{q>uSfA|ZHKJfGgDr7gB& zyF2Ti&eq`VP7DUMqG?H`u?V>I4Gd_epp_{NJpO>6WG7X%t#{ihZPseI{NTds^!u6X zo$2ZtD4mSnyjclwsGmHu>!|P?Rhh~*jq6)&Plq^B@{KmBRmlt`ozRuPj>RUiMRWZ7 zvGzR&5;)?0_3oV2@(#0cjwqnhQ+}-FnKB2jO2(mUbZSbc%7xVD{mIn%`8nW$LeM$) z&*)oObnj8f8x9-;i6T^ry&qi)?TfIkO$-gED@8&y_s+sprkmaO8g<)NfmH}XHh^hM zNj)9>B4`YGcZycKX}Yne>T$GP#K$hAI`}pLIFjByfR`^k z5bbq1Dt-_)t*MC#tEu8sOo+IQCqF~HWH-L*-#w0n2e8ma1+ObAD7MnP#T4tC<@cah zul`gzQ|V6+t16o#z9IIyr{vlq?t1TRi{5{@|5m@QV0?U>(+(>Th>%I*Z>UP1wKi+t zV#uWcZyi4ItK z@)VUu!)fXw6Jz2`diROpc%G4^Wjr4Du^e)ZwZ0h{E9BU5X=!9q66pWqI0X&L^%;QJ z4XDN|J&YgROJ*@SKQWhIKlg7d{}}$x;cjRFto=owO>qlLc%#czQG{;>@>_O@`(6lQ zwggD7v;=JDk2ISE?3Uv_)MRdq21p19Ja(H+x5PyrPc^C)nX4>pyAd^eT_zGQLO=#l zKJAl40#|Nc9!OS{dq5H$iPphFNSFU0q7LdE=|aXH?k>>zh^0#4<8wn<;r<$>8&KMU zpPY+7t)vVog8(p(!`CPBF=_!?a(UEH+mZ3{`SO*BZ^(PV@>cz+8-DDp^V8#aMFt=$=FwhJ2e%^e?>E>G8c?1w@DVZ!)+2qyiT_Jhgn z54)KyIq;M7&M%0G79Cmkezz04HLp8Imn5>20#Tz$LR>{7HF_ z`Tl)|MEvH<7*Ig!WYT&qBd*f;BBPd#*y_JcGY=nid^RHQ;vyY-8WBod>ama>CM9qW zl={w;r>v6`M`pE&0=b0q7`e#Dj|X#(46L-Y@87TdS|cA_o19cmPk{h@El(OBkHzVF zuC4`iM+3cH`898=cdGCRC|m`T65o_*G~TY!6c`4rt_thxkD~H_1=L4;%J@*4$_}*w z0Rb^FrwgE1Zlu|jT|(l;%3IKg1*8Y!N_3p>&x{2i56A=PR?gN%kUt;ZvjF% zx!KtO@I!|htIJkdZLnCZeq3%_*yxLo!mqBaEmb~{pl4@bh#W1tzV=IpM_H&hqo2&S zT3RFzC%B!rcl*?WMVnHTb(YhX2Riai)JjpWm}O>Wf`Y<`bEJOIOX+TFlv*I^dkv4Z zuF9`y(s2UeR+Sbv=l_{xUR3NGQnU>NY44y}IE92hb`#4K${!uC2V?l~`=K zXm&a1E=nlN1XdU`6WZ>3VId;reA)N#@OR3z8g9-oj3CaxKj!*D)#>`Djlu2JUWrFm z$8cNYKnm;C^@+l28MUXz<64B)|9J#%QRW-$jfdu8L3#}?zsGr9C-0JAhAasQiHCD> zkrgP6?*};bNSRJL(4#FUq|(wdf`US`H5O;Il%xEgJ9`=bP#tm`Wd{YF)Iq420o&ca zGf3Qj$L9u$$9?}p)tg#@0o4Mcn`@AP*?$o`+ZDn?;d4yMvcG?)u(J72mHvN-v5{d1 zo)!Uc$UAU*4#Xu9oV{5w(cQTx6@_D;Hk719&Xs_6Cs@g&Cc2%oRU&*^G^PjtcV1VK z3mBG<_=^exAE^NG_tiB{NZ_Ll$Uu^D-_@5A5q{$M=N1oZte7K9AYOa=ir)x*1R_;z z{Fo4-SzHjfz100ZIPetm)SOFnOYkSwdVIxaa1w)qUT`Oop8nV^O84hM5Cd?tnr z0{Vjjh){SQyya&;$pUkn>8a^d;g@i~`#Uf~_wPV;FwpnUz^$V6^lKM|EWK!yypco7Jm%-gU)3bL3GP)5E8T7JhcaSNc;iT8@iv75=Ozz5So{c$R_nBmgthb76~*;bEdD zD3EWfA8!G293^qv)CI;oT7cmcPj^dZRGFTr&uMNQ-A`j;ZhaG#v-6CNjEz1U85tV` zW~(O{WMnBo}%C zNq@VO99?s?-X6zHJ{cTQEf=aTUXCv0e?@{O!z^*66&V{Zgim?s2NHbaihvk-)_KR5mC)#g2e&Lk`t zFTVZu?|8=l9#@j{N?eGA#>W#uxRvM{Teh7(J}N2?%m~5by`dq7xOlKxdfMMdq5k`* zdnE~x564v>U}$`Fys6qckQi+cgT1`GmazpTHJd6|lP&5C!_BTQi7OQU%p12;9vuzs z{_#2ktnfPE&(G2$2pDM-8*Oa_YyUimR@Xt-%}+V9VCCPR;Q#v*v`R%9)5lLAjurlS z7*e=XP;jsuxG4+-je%RRW$Eez4JO!A2<(5mun3-YuNkNSHV{+Th5Vl#?dZ$uH2pIL zGQxN;&*fogNx)v;d&Tz(?f!_6xE#R)4`JrdK1s>?auWSBgdOVd-1!4JiLT=ap|RTN zp&2dyJQuT&Rs8Sll>grC3Z79M2J-UH?j;ef%^`yIcq-N?b~^ldqUGN)GBDtFPZWV( zBF9An#c%%1I8gmN;Xjjp6hQ;q`QiA^H<8ds2bD+{{T>Nz?<4q^w>~h4|J?3>29Wva zJpVI5fv|`T4ctC0-@%{^*WnIKp78qOzxkY8BX}@^XMOm8u44^fa5ngnh0Xri7Xjt? z_kZ?B;GZq@gafO(7e<2$StbyzSz81TMtBratdaP;*nbyt1z#Zn19=4&3!eCYUDqW7 z;KlOe&(y=#;d}P>_GQiROUwU<1w2}l(_dj=_#BlPz*KGgnsBZzE#1KU%L|MN6U=FQb$T%%P9d|9aU zryOv6p5JH0BhgMxaI-LyQqxfqbMYY$$*X?V|6~ZAQYw>S^TpA!MGxB??+|iNVdw+H z`!RnQ6Eoe@{RoT2eF8B>HMtS{I2hZ4Z^>*Gi-+k#<+z*l$HDIj`o+6(GDgYmUwibh zkov1rgDaclvIS3-4mD$|nX9BGTHd`P@fNq9xWhGe@QrygJ${vjwTFY&6f?oOi_($j za=sa<2tk=}!xE+gJ7a3XSO}XxQqJkB)g<^hs#YtLdM7>eI56_IhoMSPJO=F_2}Ag- zlM>p;^BHR(CBKNVH#dvHgqRqj%_pkQm>V1j2tjZ89Cg&xI8`My8hj{jO_ZL)`s${~LrY`At!EE) z(b0UNTr^6>3gv`b~$vp@%WrM`jDXDcdG13MAx3E z<5*5#=51&_Z(XlZRSU%nZRb~oQtlO*;jXHUP%CXu8^ago$2C!t=xJ61K9b_@n54`w zYG?>yPVUrHK|({vqrw!5ntKOD30xd&Y3N1Km11M#9W16Wpl@BTeypy(iX|6kBIBct zP~mV^RZ)#Q+7K2I!+**(UT=59Jd-#kCnL)(rckk1ZMx6o%{z?1`7I!}Ql+IN|3d-G zV`72(8kDM^&M=aOZ-gu7TKnUXp@eU3HHo%#&-aKhjo>$T9}nnvy# z2Ul8n1!b@J+7e#R!H*xbiQ`r)>n#&$pJ*`KtPlk36z|f3jKcosGlprG3b*t=BfpG_ zkAE2=J~}2(Fgk`i^zvDN@!1MD>cK)@_`Z?;4@#$a-w^&pER6Kz2>xrBkBET^0Q^X`zH_O8MQMO(NI%U)6x$Q(LU?nXVAyfY^gjA zeuW{RR-YlgaOS#&6kYOQOwdhY6dM=s9Tpd#5EmQkJNDt+TcekHb?Yp6PW5}*BBzF+ zrsTGRV_Jik0#)y?^PPFqbpn=!u-8jX8(uYV6u z69+OyjI=PtY=ZEpyMw{$9YOOavtJ4CDCe_a-J>8L%xSbbY_^WUIObeY-Z(?*$c&j~ zVSgTU@gf{C{F7H9EL4$KQiHsTQo-z8Muiopt?S^Tnc$KYw<+~`n2herJ*djSa2bvA z8%-M&FkrRx8*Vg_w<{*+%QJg2z-D8r0t{(9==<1Cf_6jwW|qcP~+)Ty&3wQLjzM+GFo3 z!wUuBI@k-l9uf$uY@K_9N^`ls{3={fIXivPgR+Y-_~29WJQ?m1>Rv!l(EMmONH9;Q z+&#oF(ocB+x5?wtTIT5!%TtK!mYT+QWmj$<11HH>!d#aIs$ZGR&HKr^ITo8uq51He z@4s#)9*)XKD-DzWF*dxsi2m3Y9mA>1m$RJD9v)T;9&BE!kW=y5ua?v4jRbK=EWPC| zUpr0m?MB(}70KuLAS18@`Fi`o0y(b6)r!aNyv5P_bpqtY`3#C2A#D6A47;V70FCfz zA08Va=3@*5{>q-s3wuifjY`uiR%f+>^8ub=uTM5Mj82InU8D#TnM@SaeXBBluYS=} zV7)4DO{7$Bjc$8!z^O-l)g0r>@HoAuqQY(~c^l;0)545uWA=Lw8lM2Y3*GLDzq$My z9wD23m(78TX`aKt?o0-b$Ol3%)3DKW%Ys8b$1s^8uU16_y@p7gR_Z=#ioQPD-=%rB zUpmH30|+O~3n+{bNGxf$BicTMXm*<^5BjoGcC}a7123!WwJj*6=vHrzM!`RU3*!)0* zQXa<+ob=Z1>CS3s?TV&(Z3xxN?0pUCEw2gK35M)Mw9PpeKQ)F1wb(+4v5A;TIJML!Hz3MxW8n|GtJY&Oe zf_?3{xBaRqp?Y9mtUtWg)YR5t%bepF1FG#Q^eHXJbgrq*PSna-4W}AxJGza{ckg8y z8#zWzG>iIK$;aRFP{42?T+CHdsSht4ZN?_pE2OOO%_xL}=IkW>yy(39QoYkq;ALS- z__sgm=v%JKHD)HhxeBCUE@MyX?Tpm6<|jc>Iz9o3bnXoHuHFZR7rsV^-y2S^H{8!O z*a~zm97+cS;n^%qNO@U_*_r5aL<|fZns|z5!iYlziCz^cl~Djg*~2Q5kg<*aIeIz5 zquTwsf6}S5$ZyBQ8=GiRdVV0DAy1=HT1x18m#yU-M>29YVmP+8qqQ~y#jGY#(@ zGm9MaRX(i$Y)i%cKB{G}D#_$>OJ;PtPZGCJTw{CeT}fTk9${bA>s<2%_xZspLf zliqrGEa@HYa#yo=^a~#7dIN3E#Va5A+}GT{o?$2mS+sGhvod!PxW2?5X_|989?44f zKm%kKH)3Q5c@J(^012$P3T+Je-oxc6}79G7BHh2yr)2T3ruk+1! zQc8+~Yao%sa8f(P)nv(M>&R_)&kUiEmEz~)!x#>_D&MN?9Jg- z_?ly5jTtnV6yrPTBMI*&hs$)x4iCzHTKy{O`ADZMF?rcDL+K>FId&9UBZ=V-`pnEq zA8IIQXq2bUTbrl&9G~#}&bAjapk@}O!-t;}hf;~u83#vST4&?m2wYR zkSpNIze=@2wO0hgpHC_%+jt|b*#z~*>F`>6|4Qz}(QB1KT=Tp6ja#8Sy#5{} zWXT_iQP>Q5{{C*g50UG*(*qieX2)qtuaBE7JysmkIEIsd&sC!HG6`BEAiA~N)$PdT zQ#E~Vf!7)g7D-AoU^;X?%Un*|8( zqrVI$dQ9k-``hR2MEVN3q1#Bv-80PDmC+6z9>FtlXirTXFQ8o}$evL{A4Nq+mUCC6 zJQ#KF#vH?4l_lpFQw)2Yjc^y22h!(jL!AWe;FZwy^;7rIa>XWoN3_}6(J1S=#D9ww zu3C98daguH$j00J6HXrG!u`W1AJ5(r+go10E|SznDbx&t4hm6j>3Ic(sHF7pq8@pN_Z2BV|GC3K0$Ofn)4T7_YJ z;)`xEvM47L-NYEb{z~j)$@e#&SP=baLS3Ali6X9tyrg;oclSZ8u(0p!EbTi+-Lp=m z7x0#~=H@hQKgW_$mv+W-I9(Jtx|g^AIPssaRcIy>z44AkCvbt?J0O$!ML>3LtC;_} zcWBy$f}qv1_2s15ecJ=IF$|5(iIsA;;GynD=GvHU=PZj=>joP~%3^+zca(C?RlDz0 zNmi5fnDb}S`d2P3cFwYInnk8A*BsCu&XADg2qX843RS#3KkZk?_1K-ga4dO;+GD-@Zsq)RAS; z+lj(XnDTXvjZ4@wc@c_=iC*tEJkl=gc=j;1+HDd$&4eX;}4`SV=cf&K31h+~to z8{^biN-C6mH3rIG6i4%K?%uNg=7$(t+Nxjrkp`$$Y>u|v=#mdF-E@+F_OL?ZgE!i4 zQ5`gyJZe-El|rxk`Lvsro$d5V*yrG6qc|A}O9VB9Vx2s18zQUK=zcEGx zV-bTXzT;G)V4|&Gq|K zczdf7HIhiqnF0;yYledJ2A3VZ1)wK0qH-IwQy zIdKZYm61PZlVYy#Fv40ljK{8z1oQiM3*^7zdXdEGu`yFYJ#0gtDBzTA4bkIyZ%9qq zyU5kx)Q>**7B%%4o*rfBV07m>rO{7nZW#OhOd*CHAgtRa+cskp5&{^L@f+^G-2`+l zD@o(d`ZITF&9d|j(FRIM2H0G(Bcu2S#s*i?Q{hd(g5}J zFt|6S!90XbYSfuh4w#hrX!e(KLv-C4tu(j4}F z!3!4+9VNk2`M%M*Q^xWT7@HyYUC>W88$i#O6QCTnZ)%#a zrL667dMok%sOXUvmdIHS?gf4m2$ z6&pR93Mq}15shAXH8mU(dp~LvEvEtZVNNI9U2%5{b=##LQsY*4i}4RK%IZi_&wdtI zTf%=}p{M#No6+R6SPuPb87@YR6UGzGBPpk^@7I!BXDwPul$_hPyI=^&UAU3`t&3H< zuhUy%K_2*>Htv8K8xOh0ffkp?CdD4pz~41I7vVAj&^jQfY5r%7VTJ= zNAa1?BE@EM$^cfQwJ$YP$4vg0R++w_Gme+&kF1R2FmrlRSkq)g z)>(Isy2hWCVgE{dj6bH0=8r1REi9*vMGcsq^O`+LoW+YQne8Nv%k9}I`ZshOSX{tP zl4B||%H4RGAZWPQL9Z_iagg<#zu%r~6(y9#U}3&`c-?|Fd7Qt&1o!Qw zUx)IAumk=(!5@G^IVL8q;in@$^q1H1?>r2TYR$WU&`pM!CP5(vegCLSN!gb+$IH%4 z9x+IjS?mKLih-Y$7W8K5w-5R-{puc*Q9e7M3hBahl_B}Qef9Gg{3bRA0F7{V$A!$# zCR`!hJ@JSYz$rZ8zG`J3@G9t?9&aupueLJT#6(*dX!n{qiNxixbClL@BQzQ;E4wkA zY{2C$7pilR66nRUYEJx~_B`n*s`(CxQS>USXIwZRFGtoJmvYWDMKR8gTv-Z4mZx*NtdT!IXwZmQ^iIBQ z<`x$VbcGeJ#cM#q_Eu|Uf1Z2b!{~SAZ?Iz(=ZHi3s|X%TTtZB^lgyEz4kujyHM2V+ zqs=GXP%mXfC|(L4e6qcV0}H{HuK;3ejmwipF{^ zJ9d;+EB^kW!NPL8j6*|DoR$5IjkND@+RHP8^-My%5VPF$;}^r#APeRMO9+hpPU{V@ zv6FhElNj+(*3Tr?4YywmrB91$iP-GPY1kMg%4@NT97h&6L5qD`tLPY=u`tv%W7*Ar z$Mf_C>-p%7&o6h!^roxTW*LC8nFuM$*#JL_WUlt>!1{_-w7eY|JwI1|z(m_c%n50+ zte`mDKEC9;>UvWZLwHZS6Qs7b3-}|TY;B;wy;5#*Iof-)j*xZvE8sc)BAU0hoxejb zSZitVit=*!97Zx?rgS)FEJYLd+AZLhekA_Pe%ZM?t(v6Ft3+$$N)w4^>8nd{C*IAD^UPXaS>>;5NoUVHp(*k$`-&&I{ z)7^nDS=vahB0mw{KHv=@7K%KvvDN5C`?uF)DbJJC^919{D~OK0C|n z*leq-clPn44>VR0l9Hk_LYmS5y@jbIgMsAL=Q;W+B8*4(lOb&F$t10+#;}F+*%^pN zBiY0vx;J$71AqfE4e_TNvnVdhlCz5n!lQAR%<2Mr*qd*z0M~0dG>v1UZ&AkP6}Vsbn4rpCZ`JTKyq)e*K%DCeXSygS^Mvy>nsm^ zHkx#@+Ba>25EfD0INfqGB`P+Obd)?V^fd(b`T|!#7=~9$?p}=fw#rx>BR#{mY zJ<0?#T+OQV{x%j6FUPFLF#l*mJ@{O6G~jzyrz6zr)7w?@0d)c~^u;)A57$c-KN~NX zoHDHhErJKvJH$ioJ2ZdY;ygwV>pUA@?hIWJe7lvIgKA;Pw9;sc{U@FxjIDUu z-EpN8t%OkO7nc&>>~LhiwERZIxxQgnoLgp*IuAa*Lx$Sm7g6)YMUd;YZ+VUW=8vd{ zStZx2zP{k^d1#gS4c}n*V7f_-KFCo~(G9L)uKvu5>q@a{hLO>1Jo@I-^zw_Pxjq*k z@Ux7zP9rCxM{O^Wu9c3z|F8ji9&fati#pG1;ZvagC94LnF~xeGL2_Pd^n?5+Mmb3nVm^>SKY-zPp-6FLT*}mzDM@@B6wM4mEWLXd0fD3(PkdS(aqf= zrlBzNccRfR0D~r14vK+QiRwy*IKttLgED%3ynkze$%;%8j+g0 zxitxl*y2zOq%b_RsF?l^0nRn~O6{*oy`M%jM)=*=9=bPPbGxGF5q*kGmZ9gP?Kf8( zZj-IEOkfL}ckeI`-?901NNdJI;nHjQ#fF9lR;dNUEiWV=FIG( zW=~N*PRfPW2-4%~tM9QDbXLeawH*hEkN6X&bp2y0cbUsF8+=7YP>md_7kPe7np2FU z_n!Q2@R*KLtO5YO{rzHQ`9-Z?I?3~d+|v0oP`Qz47)gt^{03)nP4zP~AKNONh~D4% z`ZVa?!NGUj{%(K0&X7`CrlsI?bnAQN(Z*Qr*UYVPnc5_jRVr@0_swoH{hRat!p<3z z`@6ppMl|SufRKG{*2Q1MKl1}o%P1*$R@N^P_OGmiTT_6{4h(6tLN961w0!6qA>@DRkk`I~q*DL-K$Xk#M$P39X*Z#V5~SA&HC!(Zo6 z^GHm|SmDJ*x#KN4Ga-&G7*(XmP$_cySw*wh~tK{ z9;*BAus+;ZI$rLXTQKH}?j3EQxg_{?M$kUfAk&9lkDyJT&OQhJMAD6KCJieFM!6-w z4cyW~z5cuKRNkEptz=dfmyLBHMyL;fI6>dr*HwKjBC|(+2GL`wP9h`^CGB-QejD)U zV*Ie`^IBx9lOs~_fD0#^As4i7in2fS1qj0M!zGW+>9qarmq18<(=jQ~e>Ey0E^&R& zU?ZS6az3&U1%KH&t6Eq&%2Ykcx#n7>c7Yhm zW#_w?s&&-=MsgC`T0>rT;42A>o#o5+7S`7`3{IPtBF<``COGgX=Uh%EL#*$O7!OO) z2z77ZL{O8f67Gx^9^cMalytOB22=0V$@Iu$wS2&|iT=O4}|lzZ#zFAYA|_r9)EA@6T4GGS)by&cN`sn~no zb^po@CPMJud@#*yG3PTvhLU?MfKz?i*SIxU$Bi%P3%sd@sUQQ1W23TF~jQZqA zqRnENb4_)RVqgtdW{h}WC=_C0{~tg_Iy|KqqQmMh338n;6oq|AkygiYQc8Mb8FOp! zj>7lI>sh-&8yh-XZK(~9Olv10-s^_@S1vAuRdjHvp_pCrqjr%pZ5tD?IbUnErZQznrYa2V9 zEH$aQF}$dJQEO$zYvb(!x2ACUSy*h4xvjsiMa>i~S6)t=EGj8m!NTQIEcyFhQ6WWq ze0*Dt(GqOSRAHXJrv-7s>F+M`5VEVRf1m*-30SJ=q*NKJX?U?z9d*g4IE;kyke_37 zT-YZ!_0InFup$LJGhdqSrleK}q%CMLOChFHN}W&A7U{IqXU?6ZZ%~vLz74%={i$0( z_>&pSS9?lR?r{%|78uFvE7y}aAV#F0F!7oPTk7%m4f)S&BBL(H;3V5mBmG{JMVHz; zYV|<;nV9E7tOj8&lPNnah8A27wy`M4soiboBs+-5YC%H>|53^m%aN^ToDP86 z2ft)$n1Ih}tv1uzv*le!bLW4zC;ij+QClvJXyJi_pcN_h= z0cqtW8Yi`rv&+lf+1g(j$O_gOL&{59@_2=jDaEnfj)?&uDu-{P;-d{!NIt(OC6#+T z$YjpR!3@^f+(6_L8AjCTFo-VHo@wh^@>XNiLO_=K3hOukg#r7GUQFCAw|3m|83d#A z>qzY5k1l}0Ri2qC2nA7!@Hv1$v-e82n9_wp;=M4>kC>RCU@Dq z=MEh0zOU%e-X zjGNsDN45H!{abX9#Ptl)E#Jn})KvMNicaaH`YR)-Q8pA3LQKy^OF;}%R%ZTu9>@=b z18Hj(eef17F%n{1R+dYEK_x%imgUale!w-Rj89LSRrW3>Qrx|f66Ov8Nk>_qj%M|U z%fM{M!55^5T%bs;Vc z##50nRUE}00D6O(fr;1f!!`q6QiTrjrdvUc2#yPUwUV-M`c~v>Yfg41CWyaZ-d7N8 zY-H>XLE_{P?YW(1K)J$O&41Vh#T#Xe($?-lfC|v@AlyXI!M2g>CF56^D{Ty+A_fP zX*2|v!}SsRfX`GyE9IEp@br5T&lBKW5kn=L-L}Nu&hB_JPl2Lk^XWS3^t}vdZXK)- zKKj=4<&QGcVX&5?t%oIM(jdpJh6cXeeRp;DHEwdEkx?;iH774T13oIG*V2G}!~mIU z+Z#?d<)aI(l7jXkWO+ve73LKPT9fWT=v^s-TOm1kyq0YS_+^PI10eiF6Dg)7%vAFI zyYE#>TWtTuJXvJ2S7DjtJ;H4~PqVqm_j#g%M z-(GyksAe5kg`(ylS~>P72azeXT9&5)~5cghOLgZ+7;<~Z% zM=&!j{w=CHUE?iNz^p2jJ*GcMnHq?qAU$=?qGObUg;irTLRG#Funs##3btoy3Aa#` zj})SEKWNzRskyT}KUx&0Xy?yPxVYgiTNw*iFMpaZ<=o#aoLJBrkgWkUH(( z#YMOKqv}GbE*&m){)1|DPu=wzmv-dmIOA2UW?_1E);~%M(QxCg`+h}aPFdxqH+xRS zI}B?E49wCd)`^z!25Edc=3YxsqayLJHc}FnWKxgx^Meu8)Fqa$o_=(GA*gKgP0q)| zZ%EymXc3imqNIm_u(TwJTXNfV`_6s>LkYudmPB1!xxxK~-86-Rj)@_hQP@y;;`S9= z2jw#6u(oI47{4uunG+|*eRTKDJh|$=cpWZ9HF&VO=lzYQ90z2#-Gd)6_sFf2u@rt$&#qUuW5D}KB-*L=)}U7 z%78z3yFLEA$Z}-*lem1ej<}3i;&AAxSJ|P9)zltp%L!#VHm}I%?Zp;K5Lz8xgBTX5 zvoh|OjkQ;1tX#cnWu>B}#e8?+MYdEWRAqT!(r9y4Pwlo`*eKRoyRXH2(mAn(9BOZ> znu&tEG`|in0INg_`$ft5h6g=Zs}XoV@v*TnQMtl=zIe3IbqcbryJlldRPT}5jK4lP zfS~SmKqvkpN6iIxbBi-^R?pTHkWq_+?EBYo#;2#F&0Om8p^ivBuWa8miowor1`T+a z0zb>W33ArOy~Jb0DcG=$`?k(pJ{`o$v`#{3m0mMHkUfQpkIpoVTyN2aCP;ru)cpYom+3B^zm8<0Cjt&g;Ul<{+w&!ItR%_VJ$_spUFonz-sv9+f0BjvpSQ|BJh~ z?&-4&yEcovyL)jc?k+7*thifocXx-PrBGZ86nA&G;_j}+T|4`JW}f7oKjsSzNfVlo z06*Z``&{Q*>p*0NhW<|>_M?20f`|tGsT}SH_cd{F?Fm{A_#dnK21w>mK4^%#DbW1C zOv>Ot|Kn5s?~3$)z6EU%{(o7T|IZKm@BROud}xpe+SZm4s2N%Ph=z9m^U&g$Ha9Pi zQbZ)~sTCA@l$egx)Jzu_7lWS*St0Vp{YTjnTsnIC%ZrP)y6iYRvfm;E|0;foL+vhm z*V8dGyS{Bo&Ov%~a^3GU1qDHFba+b4p&mjQ(Ukb1?ROpcpAjG-A(09DeDbB0Lx@CJ z^=;lMad3N6BI0;f%!jRk2#Xxt63#1^TF_Ly&^BoI(UJ(dy1F_fo2!f8G!$dOLr0HK zO^|toum4-S&ewsJCt4p8*NAq1*9kYS`kuN^!5`o zOM8nOJ*>bJ04=_@#VZH^tz;^#PpZxUX!sF-&=1=+e^?CV^s~u z|7rm{puOC(b{OQ5)hl#ek8kLuq<#SoU}V%ZB+rwEpYmmQZ#WEVgg!SdYhzv+F`khh zD+qL2tu|E~61w!F5OF0XCHcHQxhg9g5umKLadI#^&la;;Sy|bzW5>3(rk9szmXutT zz@mgWjc)-^7W*{&r*C*6E8qnAk$i!K=yB{i_k2&J#PvFg$I37=x?F#*bW5v<7Pp(U z3qTXK?jnxcAyHAX02NSt{!Vp$E2b9&a9?f|6DA#@MtVrCj2;kZGaDnlbE4vyo_SpU&4B!V6eT>%g0pU&dkCh?b}sK$HI{OBjx(`GBP^4x0IfNsZWl8*8$y~DWGU+ba)tK`jtf>t@I2xX)PC{uAEl$u-);cwC2U9of)6i8j;^6@R5*`0lBFF0joY6i7d=?$lgUoZSb zc=?uG>M`(Aa`3sfZhn6U>@sr^M9A~;w_8wHmhE~tsciqvubu~#eWm}5z|U&WV1^`4 zO37c_+uyTWYiS4nuy-2v@OCM9;+a9bdalm07GUDOnwkE|?i4+=;`QALb7j=1oSK{J z{|MeZ%8~F`%I@ZiO#q{0N_mP(I=0{qMf6s{=P)ND!)K%8wL~601>JpN+z$MZ4)vA! z4-+ng5u>A&aCGWz`U`*Wfn^HtEJ(0Mrg=_`qqZC755zdOudWii*7GxzFwABL`zNT- z(9oVPwI5t0P{ol6iLe!3p~a?U;yjUlMLipvl2@F4 zH!VOUR!#ctkH+;q2J{!MfsS=vkN8)Qw--L}i0nO|#fma(&NFOQi;k6WbeuX?W31F~ zWuc(BZ0A(E4h{+qM<(d;b8&tyt}KdkC&@;SODxmge5ZW|stVms*UQBb@ZMY7fa3da z@}C7)*Lr(9#xV1xkdMyg{cUOkwHm4y(`}{7DDYJ|vFk`R?xJ5kefz z`0eFRzcmJ)Mzzvpn6?+t70;yCG4|&V%Bd9i#j83N_Pv58VH z8NZ9BnEqSL$%1HFDf^q7?TQ|tj~>IVBYWjEJ0AeF?6tRsin=lmPNb9D^Wtxlk2jj@ z>%yCALf-e7pRsTWx9_Ipb;ThYc{d6oQ&P+U5~I{g6rLNTCm9&&j}9$>AGo(20|$$q zfkD{sMKe2tSfLAYlTuc8TA#9Q34`h*D9hU0-vtb;kB33OOig7XWsHrLwhsRl{3;-i zQS%MqD%Ch$@fVfRLQp6u`&L$vL@g-<2Nw=9*0r*5bP+3qaqIf}dacVhM>BY5A5DMJ zwWxU@9bN&RH|+(yJw^P{(aFw1$RV_lkS_s&D={OzFAZJ%v@nbxCLUI>&KJ!gB(%79 z--pJB!JkyE&D+v%H;>n+*Jt-D#H$8(J2Wyj%o?QG(mN6X(#1~yky-kY5rg*snBe3G z4kk)V1s${DnF5WVCPg{?a?cN5Ddwh5mE$^WBfr2Q@5}V`7~#*{WuJ4(NxF$_fkq!d zlwj2l*iHTU6OrcKl&Z4&dad_D;UAs5^R<;q0}<-p;NVLD`i9V^o(6nKYMS?1yq-}% zzHiQpf4R9ws{-4!g3IQ)-GhVHrlzj``r7tdzgJjiBqSetM#f+~T9=sxRMfKRz$&A- zvo+<^7~~jMecn!+M?@Bs_3@~^(S-^<9;+MtUGwq3THbeO|J;`zS$jJze&^skJwGaB z3CbvoVGO{<-t3LyVLSq!4{g zP;W)!Bjw2ECk`ORFEIDXrGkyH4_7U0w60ATSpV}6eKEAx=A)(pXtdww7*%m5Ku&^HV zWeeec5SYl@ST^i+H+zig4D|Gok&(SC{+BO&eef8{Q+&1N(Ms7uxX@DQ@%BqK{Q#4R zf@1OSyM|_Q-y$M~DEtRb&K?yBLW|krOM$^gBs^wgdj@8z>tACk;IIuW67>XmWQy-} zY;7l?iUk}L3!5EIU6B{3a-(wTT(;`vjH6E>pUmux^y^O@n%r*F+S&xqJkM5_WWK+z zy=o^n99BMYa5$Q#f!BOMJ1x!%gMHCd=qlOMX>~qO*VN?Irm+%W31wMa`-8GZ5u%NN zATvJv@6TnWCPRdJgFCtl0f5D4f@?`EfdHXTj!BV%{W`BIPF2q5+~m9S1y(*jg8DOn zM7Le3$HJ@vb@)q0J=KoY@abDQmbGR&jW)tA52YVsL{*g>$k(yQu+v1eCx48lL_hW` zagyhw5egQ|d@r<-{TJ{~H(RaJ5O&AdM59bohOJ(;N?Jx%CfK@*AtW>$@5neNUJ?ZMa9RX5BZd>>LVcVvf?GEvPe@h(jqA!QQd=Iv-S38a;VMXLz)Kr zwHCKE{#C-bFhpb&LRhcEs@&pgbO{QBmX@ZR_eesJW2y^wYosE6KKrxuZIf9--g8Er zuiB8W?`OY&_=BG#+Znt-@Zvj4o)U(VTC)kGnVk2MlG05rEqTb@A9i-OkNA^Q8ZEXU zlZG2k1)AMMa;S#XzDiPy7ep|L7b7Ad*e(|!d}V5Lj~$kqYRfn?5Otg~>b@$|u7_ns z;ws>V#MK{MYjwvsLxG3)czIBE`ob>zcOVA%-Tz2_jRq6=0kVHjZk7j5q7=r{bu<;|Z;@+T)SQ$f8;6-Yy zszk+gjSni;nA#0J6e$i}72IOuDzo3?yLsTZ!9&05w|gvgsCm!iqz7`_e34?-@}f&*R76!>oo=U{Ua5L{ zcl_~exddB&GCKzcx#8BDBNt+c0dtXxu=m|=_bXh9l~n|Eg?2OItr8u?&%S5(b0ubF z@>2E6DL^px!hrW_bwA4xbY);Sv$LxlK=4bOJan(t1p1h!f;#E!>G6-zoaLklc#V}x-DXbPoBeBaUmtW+*{8^~VDmnmndz4oazjH7v$6Pi?EAYr z00a&D=l?v9qAKSL^;%MQB@*U8^`-lin9m8jKvR?2!B`b1LLObK3EDz(WJGpqN?D0g zsWI2f-{C31gv{Q4h)tJ0eBln1vY3^sI4GeLfBR9Yt~`c}swbq^vhO3->Gw(@bH3cH zA}_C}p@A1wWqOS(E^!AMKnXE$6B1IZzg}*G?%#z{9mS6yX~E={8!BaJNSW(Q z_et8_`53p-c%r1%wcKis_7*CJi-)JCtOkgqI8h60e@NDmG8hHrjE%p5ejMpI0xFlh zjEt8;c_JguM3qF)5`{c*yt?FkL2;=MzOo4hF(!W3Gvp~mxXH{Z8_>}ffaMt~we}Ov zXsXVV;|0d&sZU$()5ZBZA7!J*6kl^QVCk8Dmx++0^hU4xIgNgKdrczZ2>JZIAgM0} ziXMszO1Crh7mbek(UE1jHX<5U7(t+7z2kKun!-5=I7>dm${!ylP~}nPdHeu%&!P?V zg(b7f8uqrPp>WuESievZlN4lK?z7hF9Z|RD0NI|LLKxzTaQ9ngtD9r5h}I^={plJQ z3~Y5)1Wx8F@oackrE)%s3Tjt7(b#zV>94oATANR#{dxQsuWGl^1_SjY9^Lurz2-)a9%>qz`N{`=SGc$Z#%f3e2Xc_OQQNq>K_Iv)xSIp+=ig)te? zHzY7nm|ytB&2|b)49>-?6B8LC6MNTwG8hy}Rnub(ws@YvB5UjV=z?+Q?tBF)(|X`r zYEWZv-Q`v=7Qfxk_XcESABgcZQoi_ER_eE3qI^_MN>ck;a5P|SZN1W{D8lBb44r3q zDT$$PB;EYKTEGMrPPxu~?X2Cu<2jwdMz5BZ;sZcO7W?o{=zW?GLMOi3;h0vMiWt)M z^*tKm3v??*C3#Bvccd*CMLkW>q!za*M?nM>kki#>q;KY$n)qI#FCpVDt9=M9W5vX0 z7^0S<+^D4dk{`eK9hrsvqy0w4N~2cl`K~j}Ca37rr%z0DL&19xr)?fv+uJnJ2@>$3 z*LY0ms;%TO;(xIdV$Iqr}@uKW5HoJq``X?6f;sl*$>46RP_}x#Baq3(U5cpAfyvK>vc}GXa-Chr)CfEKD@c;%EF&-Yw zNW1Gw!_HuAuP!o~a8N7#2$>^c6`m>AUSKe+>)*wtyYX_GXgu4+I+;c{^v!xg^p0~-|-UtWKR_L&Eh5-NeKh+UvP>_EQ2}OAQKd`emG&D$yBqXOX zyk`lEAjtbeE($MzR0s!yhJw`Y`aC}nlU~wJeh3fmE-A^2#avL7FM=2oJrog}je9FVZeqb7Uh?i0;%r7TfERkrd2$7j;X(?%*~tX%ZsV z7B?xW>&ME4X=<5CdueIGaNtybL(9_q0&54?f@Yp$`|K8Bl5f)BZ=0Qw#Ob)**K*4V zDdKDF!NgHlQin_&v{!T3w$b6MqB<5A6C0KR_m6-Dy%@)Z@rB>?iW(>IQVHir(r(I- zU>&BX^nP#PW*6KpfuQ%Ms8>#XQIR>A1vbwq4(dOhJ<|@_30ot)~7L;=iQ|IMdv_2Og3QxMoyo*o1=B#XP}iSW((hL?;%kr zq-JKCm|MGV?;^Ba4l0sPskA3EVYI-PpzpRB^@dqnS_D;M=Oq(0UJ0~Uqg;;aV z973-UMmq6=#o~XFKTffJp{i5%a9sVRbT#z&;_0kR_&~~q9+K{hR7^=Z2JzAo)CSqN zhpqtauUBupSGCnUH#av$S+-IKQO^1*KBcsgrlTDM9~Kr$L`WztB^Ak@WA2{pr{8oi zH=1HtVUdVMtH{pAHas#GWC%fda()gF5RMz*0x5WT zn@mS>gQ7nC9l9l2*t+T-1f8m6Y9o0^!5 z`o$Wr&nTiQC~@%SQp69bQBiZqiUHI!ze-U^(f?~}Z1uW5etASP9L$-wUFm?5O32FU zaOe(V(X)X*SnCRmjE)JlCF8P6(o9$cHxgWyFk`wPJkRGl<}*F=EDQpY)U?DX!tH~T z`|bW(FgI<_G3mE`J=y=`vF6YP`WlHhI;7OMKG4#<7dOqhwzr$AHYuv!8IZ^msj}}B zb(4xlj8@3x4a^Hb%+5;aFn+87BaFoER^p_Zj3l;wuAVnPau<^rzTnr0s^aA|`eOjy5t3J9`ZbE+pne*=AV(8DGl?Gpdns)C6mchnY@%)wx7DKaKDe*GN z$1YWIXiLyA1}4(9Mhl47{@Gbqu5PUZZM*}|@b>|#<a@p}4CW9$DSMj%H1^?bcudSCzvm#L3H zgGEpMi!$z)QXrHkvl>LY!`T{d1wDGieSa^HNoW#FOn3+i_xin_J#g|7aq;>;=wbSO zDZO^h%8!a-?`v9Vv?C-U5SteAylN89!lY)%{T3+9WxYJMSkMvN?ikWJS+P3zyrhFLbn@%sh2GHMuU(j+NmrpCVyOql%ftPy@%mwY3#Vh-yp2 z&(8i~Qw8+AK}t`(n}S5BYp{b7YRPMZB=PEegHlyfm6d%cu_~F3yRaUDwSdxR*cHTo ze7cpdG-wS&CAnmFAp?)h$&>`Mi+GB^Ov;>5Gqj2nHqCBpEsxjxfr)zUKBl)NKd|0^ zwplEmGT)}?`GWSOwoTNne7a|f6E)%zDz=A!ga9*Rv5`xK&q!U8lS2+JenW%7VaUqy z|7`rpy?>XKG+0bwx__)eI+$SAtbvS>_*$FLm~TBR?tiKcq)=gYZB zl1)&8^<^Q+)(%`RXpi|-RXOn*P3GsqH!O$oo0ib9exMm!4XhqWMZAhjN(#&Jm;fz| zVv{#FxX0$v z`zOqsCL#=F*IRy|-jT~h=siC?9UUDZd0Cg~gyKIhyPlMybU-+O;usbdTw;7`+a=-~ z7_`upAm1xQkHf!9MiU+vbvG_-unzHOOKU_TMv%;5k&#_O#`IFr$;>C|KXqH3etnVb zTU^A?b^U-#TB)QWhb4(_Ud^F(b9*b`0)Pz@bN!w-KiN3*W8$1$9gVJ`MBt_RO<5VS z#%=cVV30-_M;slFwmm~OH_e!I>ogrc!0S^4r5^6j%zP1nfewJ{l9`!)dm7sO3V;kE zo-g>MhQ>ZU!bR-_1eV{66eM!c?7mE72{mtuY*#2KoJ)uYZ3zU{R2p=mLZYzH(INYy zMajyR&X?`!+KmfrZyuaLy$v4!h#}9eDH^&ooZ^DtnKtGTGIGS$nW_toWycPOO#&8KmYtWA3l+ml3i4oH=~}SUk|>P{+^LQMY_7ePzXjk;L(Tcuf)zhnuBf{BTV-nV~$|IS4?*VEBqq!euPZnV3+L?D{C z2m097wCU(&OW@E2{Nc|yQlWx5XXPnKQnV`zm=K!4+jJwc9DThGAIqa@Q|E&*%cT;U zvrr_r<5d_4pAy`F+<+Eepm^7jf5LHa-T#vbMI)!B{6^%TR4eZsu%CE7=hu{;^hEuKAu5>N5*4uVW{2?snj~FCPG%JU)zvF{;&TU zub32oii%U?Nw(2Wu3nL5-qyNe1Vx{ySiNpW(xPB<2xInTPG4kS|TY~=s z>sA|KqX&9Nvyx~^^D$Jhn-lo^3n!;K8p^! zk0W!8hndPwn&9TvJDD#pm4$Qo7s0Y2~H z!9#-D%3_L+jjem6gsZ~d%m}vqx##ivFcJNql9Eyv1Su{qAwlpW2|{ak#kjUUgcSv| z3=u?|i;$4EAv+hFn3C;I*sVu-&Ni5+D*qaYSqS^?Gg&3U_r@h43yp$5u9XIB@8^L( zl)_{Fmy;C1=NtYGU_ZEBAj7xB2MCOL#@my0jtQ_RpD(QpT`@|2f5k&Ah*6Z6ia|bG zs>KWZ3ax=&FJ&LfU1PlvyHtDN<>hJhff{bO%ONZdf2qyWxwvBT0aWo0#E`JcvNll^ zUhQ#h{hm7ifP}3ssw?pZHg1fmG)O965}RGAr|@_zr?IJVg#el*4S(jkCV2JoL8#?E z#}7J$C-J+=q^;>!(++o^g#yd(i;J2`C2@^$y+q+v7auChL6}hMKaX2>$V(99A!{6& z@W{l3|IP*B>nhLz@6VTzA|d<;c}vk}WMvcrGpeocNhL$T`B!c7%#!;{4UL39LccsQ zubu7!?crE^dHJst`No*pe%*f?nCS`MjBD-x)dCKb*Df?I>ZLPCoPv@L?KRV4(d)v) z1%3_|46BGDZZV6}1&$^Fg=f3pITucB19tx5GT++fGZSSzvra?VQ&pM1k_>B`p7yW@ z*a9aK`&|r zdLIpSPVU!(T|wU~Np*z=>&6N{Kfk=B!|z3L7)m&Jntm_|Y$0ckczrWqA#pI4D#x~R z7GSF+0$DU3Amq{HZL``YLnr1X9p?Bt5$zK6d2>%(T?tz=7`BD_fyF|-C^mCsWK=lL znvPz|1$zXtQJOtu93G^P^B}DD!Pm%`dYiu2VIea~ycM?v3EE93k%00y!pcIx<^*lU zuJ<0&jyXw55e;qDE#5MoZDud)tF)J9d|4bMczsLle&;oJFw^cU5XXF3# zQbPs79{IFy_>T|;eM{b}UrBk2Ih|()wKD>KyBHwbxZcJQTmL}-acX4h@-(quE{<3T zx!h{1r@yIZg~vY6BrkNnxykL6(8VfZzoEfBDjLx-2FM?+&AuU2{*Ti%57&U*Wu}rh zH`pf)2L6_+DmogP+pag?D&inH>|C{Fmm@@GkC&$EcCTAw6cZ%}be~Qzd?`#~)gSf^ zIY?^aW265J!Bpz(-ILeuz0P?)#V6BaPm5bHn6zfXRdMuX|umqE$CX%3#26k zmcNgp5Tlglj8-eAs7kPnQZv^(y|3?>Q(#G{AkiEGwzejbnhp@8`aPkgRqu z6@Sz>9JzjRHLXxw_7O&Yx4(uk1+^79DObR0C0P!X^0?!vqx?xR@{^!X*@dq2iGAsFiC^hiXAJqOJb zq@b7n{td3>;EAqrb2G;(D&_TX*bVf`1dNMQIP#s*WG1Z|8~R;XzCb#vn+71bh&2Yq zMn0K*wFwD}?7|OW5JN&pHMzMt%86~XTYqOHsZm@|Cg?S`sF)4e#C4S$6=Wz{W~AX8 zlw3m28h58Amw!Jx1pE1WNYhts;A_(T79-D-frCr9{M*^Qj~rvqbc0Djsxh{KN1UvVaH~v%(zPL zK{3>*TrRScC+toprzK_UCah4BT*!M4I#2k7NzU@J<(xQ)U3}`=S3Ju^y$4DMF>77B7;v&)3rWU6MCe zF&$AtL_pA8wiF&bG#S6S_!HkNGoDf(z#5kjsysL!VB&{@gM%jz4Q0COD0Tx|C{i=t z4S#K*L6D^4;^f3OA0XYn&7~+PEUZ4(|5IF6JT@^JJQHLcJT*K#lAl!P>M|(|*0K!$ zPQThh28sMnwek0Nn4Na7VZdF3PAfOMPfD6~5r2(z#b}8qQIPm#=1J+L{EabXAcm-N zAV+O<9uDYPqIv{#5kOPbTP6bCL^Q;)bN3 zx&^p`=8;H3@;pWNQhtSC5Zyp^OL3x2AUYY&lr!G;kLC@!_~wpVS{{+pFlmW>UYs0? zxRpU)P}BHFXgoJHA6B$nYt9O-uES}s>>5Xb4JqK^H=HL03@Xmf?$;L=qPo!)BFJJG zuTF!&gczN|QpI?cQ*`kD^Wzg(Bsp!z(Ep6j%yU!V;38NkWj? z{a(HLOfGAnQ#7Szbz{~$$JYIh>nxjwBC!TP#*qH$@S0^hbM3kWo(*Z}`ciof5d#Bi z(but|u?WS8#+rs?OwAFJr%fU_IQ94Vz~c784^Pv>sH64;GZ7QsUE&fM0l`fA`p1u} zM~`yiqL<2TgKAaa(leoPts|K)wXvOhiak9AvY0atzE2BKEG!}|)2d>*4J45TRm;_&$B=xn+9dT7dIOwwf8rcjhmPEM`t zw(bnz2WNReXG%?L>);E_^(76owd-x=vk6$x^YUIeAgSsd3IHvl%yV4BRO&Gp?p}68 z7WkHbSlyslT&U*YlTXp8P65HB8m9;*KmS}|=-nI7zm zVwbv=E`jyh`D$~!*;-4$$?zHTqhDgaut;A&QbjmWXfI19Yom|AwFSMI-q1a3@}d8y$8+Eju+AmjBAy z+rhCS#b%8mIy*T#Js^eQ6dk7+pd3UTAfke$anAFHZNgZxu^!6ezw!Q!wDD(&If;@Rb-9tQX2#eI9Fa11ZGA zl)3>YDAywAH3;32oUO z44{Y@o~V!)>_28c5s(~%lz@m5{t2|M-_Y9c{lnHt&dNjUexKE@8e@2Hru@yX&T{9ys&{;ashk*Qilbb8O~6_hbc97L z=#{t90KyM=apEuo$fJKkrw`AhYAv?BZyTe_qK ztrP;M%_6)Tj5LCgecwMNSD>;n=v@b}!2)j)APb6h2f}zh!mD~TwdT85`9ETYDs^_y zx2`inbpJnwKzKAXi7{v+GC#IXmReNo7fxlisS$hQ-E7C5C)c0eZa=gP#}=yr00{~% zO~|`q6Ox5Ai!|1rM6999S3;s!oyj2Vtje@hJxESu9`^RwlALp`K~HBA+ePm136EK) zqND`!()3@4ui8vX^5m3dG2cZ~$dd@}(C!5W6*`}ctnAa_zOd77L~^;dwk9WJ>OP(D z^=4k>>1nP(z4e9|=oDgrC06Y|5e*hC>X)38l{>sGdGLJnln#?-C@CR?OgFDS(Q2#l zmtkv_ZTG!)!>;YvZ+13EKh9=z#ue~{b_XY6aB^~kWiad^q*e{86+19R0kr_wfq&?5 z;t@zxF>-Oi*x2-j-NG9J+%Q|*i|029F|qE`6&^fh{i~}-BtDqO`jdHcGqc5|$bBZZsOT6DE;gfyr;Bca3haJlHc?2sbLMwiX*~Ab>%QI(QvD_aai!V?@p@T8 zzQnksS>lQVNhYwUba^WOKF>2UN_MCstCmDuTug+H(Lw%YTh};BG0h49zFe21>AjI( za^&;kUm=C(z@on^7iO@t>rXGqrIPltw!MC*S?9NiZ=zfkBT|dMQp5ln6fBv7h~IVQ zg?4>G%C1$_La=MJpUC<>Q&d~Q`r@x73SgZIv{qAjzy33ydBlH*e0j}vu(YtKvVKx! zFa1?BG&UlR`u+R&jLb|qSy{2AOSpQZ3a2l|lz6o;P{JODWOW~O?*Jd^!pWe+2WCUm zV;~-0euGTVBS3T`a)g@T-B5ON{q$c}_YBp@i!DQ0-yfAg;LE153P#)ZE;B=^@DZ8UJ8drO?5g1u^D-wE(nHZP-~F{HQr_ zpz5?1c0?ld^<7j|@z|e*ONCrsAz?$;QR|k(Yryl6p3Y_+5iXv?+%T4u)nN-EA@9FI z!9hX=U$~)re`x$*&}vryL(STbUb;Buk4_!n1BOoQpPq(l%mAkdz!1AQIPAl?7J~&l zRf(d#;1~V%R>jVelQWQ2c{iG0TIvl35AgItdIcw~mAQ4JD4-#xK+Cdm9hgRfQT-P2}X@r&*pZqO(;~#W%w7^l8w9;vR znfPm{S5H8|>;}zN)z_zu$98$L^545$8bU*w$&)FuR@oN8%c{5c(fp@ZgF!~NP$;j{5J=9(G%!sQJzu65Cz^5%1Wvk87CIJ+ zr2@wtSTnOR8&eECyp)W;Hr>!90#n4B01D}`BYwc{sMYcekARrQWj2-G; z+LD%SKpvZyf}WBT|HQwbDM>BNeKvl&fT|=j-Q=|VoHXaG3h)_t%LpjVWDBb=Kkp2nkoJmw2knrq@)?psQ#lp zKLn$*y50HtsJ5tzFE_V)3e>vk>tCQY^;K810TW+<)i+qz^L4#G%Mr_Zp`DQ*jK$e) zK|x@?4SJs&DI+(!+Tn>*R4~1Qx(}{*PLDNvC-dbq3kwIhf#rM?O_gaa$Lm7gQ=P&1 z_PU*6o%5~lPCj8AJRSzh5WFvlflflh>W z40(qA>%1rHV$KLA|Bxy{>qCkl-A*spo)CmMQsEG^$r`n2;4WA3N5Vy6h7^I(Z}Y$~ zq6zss2}eafp&(d*k0ax7Z|tzw-g&<13UUVzm&?jxUjeQ#WveOV#Ya%oL*ZfWOb>`H`GJ zDERf${h5#?0nbmQ6TUW2A&*J)sHJ&0NZmn&oAGY=a>i0Rq|?E0V@r$m$^JQlN-i3{ zX5g_668_fKM$8T@ine844*)+Q_arA9H^&whi~fvO5xqUL6P90#YEVQnTgQL%VQr@A0&#?nUc%o+=4)oF>ZXJ7L)#D(BpM^Sveky?&50A<1Zw< zu392ai;M9~61)^F$QPiv+9_{o!vJ%mk2 zsD0BX@2T3GJ3rC=bU8C~*2Kup@#*IGfP7k2MIpJdaUMnSmTk#QZT7BKB@Q+Lb)sCb zxXKsRkd-#ao#cO*nc({IW0IQpViWl6C3Hpmh#+}Zl2f@Nss8){us4g?hT-w3fqzX+ zo1bU^1GIdlq1#f-<6lpIblE^mhHCrojBl%Rr5d8qj7O;wKS1*6Hl}lF>u~&3M{Fc6*?c)s8pACK$fGpYScN9DJecmKcrxB% z3~{ryJ3US_1s*9gt#>+y?D23}cO8?@;)Scz5o8eXe!#%kx-i5@ofvs)6CxPDy}S$h z_pNrbcZNAF%RmLT6p5{+xXR%w>Gy?B{T()8@?G*JQCDAZnc&6Rb3 z4lTTY_3=W5ZYsMiL;ttgLt_)?x0lez>x0@bJy}^y{+!G;5>uKz@Gz(i{r&qlf`*co zp`VJ)XXLS~7T1#{wTi7-WwrR^G&8&P$u1?44|JanX%~}$o1kK&Nm*XL{F~M<`~U$& z=C{c$DkbO^xAO**l?EVaG}VJR)J&mbQAuyDxnh1^{(OaA^mY(=jmvPN?TWiYe$fPxauDi3&7LV37ER&xR?)+xl!YSTuZSNCC9?e>Za(j@jfsw~I#mr##j z;4@wGzA0<@B%*(q-e}*|x2OOy{St2vXOZ7PUV)B{5B>XcKjuVhN$X}_)Fr1r=@0VA z*S;ZTi-uxXCNVZPwmn#p?T*!H;&`V9o0XAreFh48nA<45V~9&WzUGNjWpxEyn#gn~ zJ!)jap5k1i+@MNiy|G?6w6#^?3f=k=ZJ|#(a^8dEng3Z3zC~C}7swcY$-8SzY)MHe z0yjrf4!OgCDv`|rut=27#9l3Syf93g%*z&6931Zmxmd1IJDH3}PvUF*!O`kOQ^Mm# z=+HR)B+~ViwYq*2Xn!Q0(kT=e852 zf2=2ovA@fr+LTY=!4axY}(dU%kOAiiFE-{88fXQY+f{#44Zjrr^!# zN*oO*fO~e4dGBrJkP8T`lyIQ*_V!Nku!2o>JD~@DUjFOTz5POkFNoDO*seiNn6Ew{ z9QKPfc- zMTU5T#^lH}@{XU_Ha39~kh5kyscfWUxT5y1X zF?x0t>+%dcw_RGrd>hbf1wpZj`~}z8h=-datVSX#BoC5{JhddX*lb<`Ok$tYr}}gr zJ8s)$u)0ae#TY`94c4o^s{N`WlY1zN)I~wa$?g5NP2UL5pCopn91?rfpDrmmIcUGp zATOy(&B2kcQCS%p837!_;eQB!BFMALN5p~POFnyp?qbdqkSY(c-zl`!woEtr{hnYG*Sh5uD# z3ha+%jgHF(umYWPjAW&e%YL2MeK1|>0oydf3ldH1W3=CSs30kHVNVaGn>d8WPt@9V zE#T!v{~)=;@A!g`$zwM?+q2rH*Xjm``ZqiPrp4Z521gAqgovOwvi1J&>&3d|R8Q!T zTJ-)Ov3#v50KRU7`=m0FMLwgYwItAJ4<{D7fHEw4!8Y}@*&^WUK*;_T{??!ygrmq` z5fz$(gN6KFLe>P_yM7(Gp#*zS*9m^G3tR}nuiQjc-`wEm=X_2{N>o>V6r5Ea5{Xj; zB46~$OeL9Ma?J!%aZ>IyWs_c!!rGb8p_&=Sc7;wOsel8N!sd6{xFa#>>1l=%E5&R< zX?9?-Q15C1q}gREfpcV@n)-GqvPk{KhPJCeI&hl!LS}Wd_s3%^jrOz(86m;nX%!k8 zYQi><(xG(l@Q4=j4|qcVo}R{V1An9J>g!Z5oPhh8DhOuJ7F1U!um$a|e)xj>Wx57Y zO}*oL68gdZWDGF}VPHtd>voeKo=D1%3_)VKZoT(6H@79S6kyeHy3!c#6-sDqX9ufj z_S{fz@ne{tK8BDhC?NFw@~(3~+_ABvsHt0OsoDgtoND@ca7t&KeUz&Q@5{cApikv^ z(~bM--$I4-fpT$}1KfVdbhPX!16}0r)5VV8hrbs&IXfIuP_wWcdewD);|$Dvd6>f4 zCONCIS#p}yq{Hjz5GK6kOs%HHNlyNA>9kUBkLG-NnLnB%*7GNW6f-@M-TsW2+b4*y z75Ic_U<*A&-Ii=fY)OX9L5L!RW3|y=Bv*c3GbdUS^a6Ey%`pIS%un{i+7DGL?mn5* zFl6g%YcX@I%MI3Yt%reuljIB|Bi~h2zGGr-ekd(11=a=Su~o)9P~IG6OYWcSCG{$o zY&E&r9j`=hwxT;pC7>hV$fk@Z&pHrjG{sO;ypjcq3My1T>&#N__SV_E+T4|h?3{CYtIby+DnW!R5R+^`gCIbxjCo*=$=9z1KYnl(B!{Dsiq>WF+wzAr zgBcr@gul%<{H}~8!2d|zqqat=k%qdzI|rnb@QARY=Ls-K!ww-w@5zIx{ypEHmfIVT z^u5FXuA!U|A0IK$Z)g8>o`{lsBqQ@_cx2?^`FX9`IlM#9)Rc6V(~>}EWyQt8c;4pj ziw6?AwS&7?A3w9q)XbwOLtZ{5g-JEk;YCOa`O`#N=-p!e$I6UVVzVF4UXf$K4OBB4 zQ%!$a`7MLEZrt1bT@kTii=`{cY@9&GZBmvz8h> z%}h9fckIs?&XRnlCti|K`(7drvvH7-VfLb|J;ognM?ahHN1E(Vo&n=Swb|S;w{6*A zEYU<*F$X()PBM#j9scXlBRGEan*ySNDzNq#jO8LNS(nabJs@8uy%~6_m4gaedErn9 zv2h7oOlJxMq2O{6tM{Mk+l7LuxRsiAU;M8Y zpj>{ww{-L>fTlPBB7fv|A@R&at^=bzJjIv`_PQH2rsN*lyQO+${!>$5w=t%jTQN^0e-(x;=Vr#p518v)tb5F-3IzZ7YkPb~#bR#w65 z3QQg+}H2+&cY}ni)Av38-?!Gcw>H+!a$Aq*;r8~r<{Wd3->B&s z{L|0i{S<0Vum1k+ z>f+k^powa*XhL-?FdrACb-dq>s0SwPI5w!L)j}n4Md0Bsf2;}i`3cUiDR3KTRuhh^S(e(w1JG;OM_A@Co^eA_qz z6?zQDana0y27iCpCQ;R3&!!U!mNDz%gVQ>kr4%%UNd*=OssOWy4?l7;<1o&jdkM}y z0)m2@@OltDx^|P@AY3DPuAhV3&!1!YEX*=xxvcEeR~bekrCr9QW)#byaB8V5Hg z+ZCaXB$=Or%?MaNZK^LAEySmOfsLsb_8W-Bzkra|Bh9;aJ&z)Vf5WV{U!LyEt!_kx ztHNVr!=AuU30lz^R5i%$$J2N#LQh0onj0G{9|ex0)>3~Y03WPkxS~+r2=Y8bgjN}# zc+CA={(hvb%6N5sZIG({S*s2@$-ua&$rYx;(NQVhPZ*+Wa&mO|zF!KZ#?#qrFJ6}5 zP^~d^zue@a6Q=gH%(4uXFwGG=eN%YtyJuu(lr$^nz}-Z)dX;uv=J0mGw{BAQE-@I2 z@bvVy4O&Z)=Zh*4K_WJjgUKrPS5I&cIY)@byf5F(V6J9Hl_M0kGoUHB{6obfd%j}X zU3!gxlpzK>%{nQ^gTPEj@WJH-qFv*5ZX^5d?03mg@I!v{8bwP0eGp|e$rzAQA; z#PNQxeGqZlJ$K62!3NlHSgnHB0231vYDhnSQCj#%s)On*9ZtR@Vab!#!O*2tXle=Z zO}^u#pffn%$AM7I^896_N%e*4dS&ZluB?N^oE+aj(-O5_c~&T1U;U3)J+E6^P$*y$ z&=V(MKLY1{Q+>1Os)ULPav$E){1WbqLH`n6i%mMETe$uv@OFBV>5YgMP&uCj{CH-!(<{5Sj{|B;XgebfDKUolR2 z$o>`F`t{eiAiv5&xWDG1h+K%xDkA{w(M5mD-KR!0BS6WXCF{j_7U~tUjkZ=t@e|M# z3Pkl*5kmhjGbtQADoG)XZa@JnsHVL=D)BGX@T2g3nE!bEuq`~x5a_o|_^N({dpj6Q z7O98%%zfeA#Qc9!lAxf-0mK}jnf@QYHd_957X6>Ue69cA{nm|1F%J(-Gc)S@U;X_8 z)E_b)*Wu{!T5ShfMC50>JgAWTb7g1GRy?)ptD1={c{P0(>_4?bt zTmXTzjlEUohhkBZPLS?#0Z1EL#_xg%r`y}xX`Tz8djxxa`kZSl{s4P?xtoKdYZy*=jV>|7bHB97uPQ?PYAxZo%+AR zx3-MRv>~XA%%;`6{d4BN(52bEvAJ9PaVVe`iL5SL&GW<(4sbK&s=@#MPIB+a_i zdwKw4L;Kz7-0ZSq)P>8djpNwSH-3-8VP{^9fGcK{h z2DS0vl2#m$PVn?=VqSfHO8B!Z(W>mHPv2zmaBsmd>(u4<%h6EPM0yRH)KFB^X>gMV3O1VC`LgTgmV{f$N(Lq-W?p_J7B)UMF)pk= zxEm^HXe|8v`5pL@0)LRKcJH5)8d#Xn(o@h&zS3NQ*~*sB&BD?Ok($r#<{iORphYyXake=na(7IceS zeLr3>#yW9IbGe{Lf!w_>+_PNV&0PQnb~Yp%Y(s( znvU)_h(Uta7FUx^C=Hfev++~&X1^5S95+1!)Al72IO5{qCX_rpOzNf`WceUodZ73gfCZZ^NqQLE7C7gUkk5fjy6W8fR@>FF66`S_kH zkic9a`{-9eScpWy$w?(pI{{$~QJ0CDdT9P`0w_j#8NCzzloFnMyL%v?&}_XPAb;I^ z=tISnm3$c)DLX0ygoqK}*ZE6ZALH3z5}fUxZN0681l(O(%jLb~nb+h3_mS}*ORbI# zp~Z0SD&ax0sf@htXRD`tE!rIROBL?+fTZOIcSG2S7J;Wz+8>5i^|>2zxHKjg#(-L< zetS5^?O-aPY7MZR=tmfEf*5+XaPV7fSJbs7;i@3q#B_AtHhou0q_$mY4C!riI9vUx zR$;gmY!}gRqTlWH_eLuWjrexxU?EOG^CPT82*BeIE!41Rw$_%{MMQr^e87QV|F{DG z1|bc>S4qP2Pg3)aDdWms5#TwK#$OzQ9UvdO}2;aU1NCo(1NOi}n*~>h!Fx;Kp^;Tco zfsuI89P+7Z+xF2^W@g>yjy;Bo^74O1#n4T3C#Nd_B2tQvZ)@{(_%1Bj_S-_CbXxa;8^yWQEiEi13JSxFq8mxZq-` zMSK)d$ce@dtPshbG*zC6mnx3BC!`lPYrHVgzSVmY=Q>g$H0FWE4R_EDk zLYbV@gV6Dk@V6szD%_SzA~wt*qc59+L3X z`aO=N2h!#B0RSGzKleN27;0i+VLuFh*?B9UAn))rUj>Xd3De3PJDk=FTz1oo3&Z=# zoiBj3BmSoLN1SbvvyDwvACsa(MA|pT0eRWZZfhrk6jgrY42J5f2^- zAsWyjm?dI7*b=B1n7XY7zOc9BAESZU$@ORzQ3mo1Rx#=??%TIF0^>0NH>n9C73H)m zl`;=hqp3_)rG>el%}vZs*8Sp=6%7f1V;Mt7SJ$u@{498HDGEx4LlpW} z9>c$MvKYWD)><9=L23qowN+#cFH7ln#1#}2WMq)oxMT2HbXske*ff7#?@#_fFp2s% zPHfZ!{v(cKZA8=3(Sym$?Qm5>OJWx={|*n+5$sfw!m~P7>x*RD7Z-2NerGxdK)@W4 z3VL7=Voh(XYzRdue`jx7>dh{ zmdN@=9r;=LbLsWekG`n?qXkHzllo^kd>;cIx;X75wIuts?~tjkuU!HZ`T5>%=irNd zJEByu$U!ET+_)%eYHGSE22%dOZdy)kt_Qtw5D0e}fpmR!y%F`*Jfi!{Wqz9e7z1tX z;j+Bgw(|hV1k<~UgaoX;lw9>>BqXvdr&U+rr@yqIqoZ>`7CB#Tw1q#cIy%*EX?FVZ zbQc{2oizrwC*blxT!1+(AYGckV{`)&H4f7|y4%wg^xh$-G@dBJS=jg*$~oVQ401g$0Ycy1H?#;KMo5#62Srk=)#+XJAHHx zk|NSX4Ica6XLY8}0AStkJvKUyjPeX~f4&>JKeE^x^pwff;Ns}mTi`?~2%>t~?|q@@ z!7_da(w+Yd4=bb(hu9q(%dNXWCc*XX@^!T+YD9a^0w!2lY3cS>9+mQ7`zNatheS$}N`>faiSw06eV;$6m!h>2>nS$j)y4i>5l7Vll~@)Y@G zo@i=j5*iA}qFGBzOA8oFfT(9SF)`6v6PQ1pQnj)OS$@$@Etlc)vdnoMib{Uom$bXH zM@9Y4?R0)_Wrc_WyJU100zq1_l90;=;o1Y-hiNUUsw%Otur^SeZD-Wq?*c;tICF}H zp;J>))6>&QNs0^KoG&$KfC+68rt|40l#Jwt&S7o!4=%2%oSdq|$B&6a(JwDAPR>p% z?gu?y9V2f0%(@Qkz*bvV>~Xy(&%30BwUq#j?UV(N3>sBG$H#w6PJV9jh4u}!`+M-K z09;+u+0A(uyN6CeG?@8na6kCf^JG!!<4Wz~RLR+DSBN02Y2;K$UOZ+=2C9hP=>k`*fYXSC$x zh;0#b2xK=Pw{WuFOS<*x3wKi4~`{m4Q`#p|-@ zFi;d+4LAN#S65zH28rJDi_U61xbmQq1b+B*th2S1l^;wU+U@`8TSQu0BSyzsr*l$# zyaaSGp4;wEwX?u#<_U}67TY`|A8Zc#LXZu(bD@yeaA*RKXOIsde6IXQGEMuwnwC3+SoNt7dmpigSb=}TJR%r0&W zPaIM$j|sLtjAvbp$>aT06h{2M9xs4reC>2&_Pd456iFCKq|33Xe!hP3`T+!Nr!BmOcK}$>+qtLc_v9Ir`l} zvA9pPKaN1a$ES9P4GtG~7ueXm?@uHGae?|7#X`fN5%(=MD}mo&Ls&)}$4QSs0O{M` zFnv{&h-RBXq&>U*(G-^Zn=2#!x;pUs9O7n3VaXIU2FYEzOiCDS`}OZypzQY3r=Xd3 zJAelzHZi+j6PUujKVPYMG<_!|vo}r@78Hg~S&PUdCo79wuQ4YjjtI@;xN>*;5FV)A zX=dACHtu%1`m4xVqg3O8jag8T_B{s7?AX{_ir@9S3|req?kB6{JI{X42T1sYvv?FB zs+_gG!W~P(Ut8Y{@LAOU<*TAGEGktl0_ zVD3V<&vi}~LnJY_7T2?}0UaRD z3;G$Fk&zK#@A6K%zx=rhgTf1bxTt-v#jAyg=%{JT>gbqRt^1AN=b4Ty7jPh_a(^u@ zC1D(r9lIM2*#qb?%qbx{g9|6Z*3?#iU*Fv9+=gL}fb(~;FopLjj*gd;-vlc3y9JM_ z`T~Ll^>jW?#;n%*>Vmh@?(R+~8i5(OF#cFnyT-x6*^Jqe1Xd&X`*V9U%del!3(YqO zEm$6vmD zAq(m6gMo(!Dy8UqJjg@_YU-n#HOqr3!(^NnS*mSlc1m5rOyVI1ygvtbmrIE>?>i1A zz*HV_Za$eF85h+0pkaEtQ*rp1PazAsE_Yz%3W0Ege97^2vo=>VUn zwxkVQcbn7sD&1i$A}!uFAZ(NHd5Mb|w|8{~Oa+n2Dl2o47GP@-j8#{bPj~4KL-?&D zm`Mx1!^6j3_%&x~w)9pf1iPfZp&rC>+a0g>D14PF)R}<84HJE4c1m~$#p|sJpDwX( zZH&J^1V4|7Anj~!;OAx=t!5k89G9OvbWZwkoyt)nDT#^1LUvG|G|cLBmb8j%1kdR=lfr5fW(`Nkxel+Hb6FE1(&ISf~|+M)34Yi!DgGko7%0H zdm4lFkM@tl(!Nc>CMWm4s^Q?`^!4>)^j=@x{TvxP0d^PeTNHneySr1Ow~cml_4)Nq zqw`E;W>i#E0Cfow6rYr|+VTpKi4~(RZZgo`60_25yZ+r3d2tdLzzV5~hUBw)nBN7E z?L49q#ggz&1M|oX9BT2W%Ylt0?6t>w(>i|E;*&2m_a;mjTcJzq?Vv}8X zVtP73-xXxQSp8Y@SSffkiasWaY zBAa)yRA&mf(bM|s*O&L#q(X-I%_&?G=qRA|R0jbv7`;YWCSv|C5|Zcb0Ecoy;wT{} z5(-KNk0)DZv2A8E`2WOV56T6ALkC5`H53uX7tu^gSZ(q~CxOi2=a*w|fNKmRz~TUm z2^e4CX|$Ld%J3D&EKix?LPW4a*V1YO(<1{NeOp`mT-oQ6E@_`eb(ojS)TAV^bQ{vv zn&~5U7rLLX5fWxF0P(uXDmk^p+(cZp3AtYl_U*^UafR%5fW^g!N^A}GIG$Gri==4P z-Gu}G6U9V+wrH{AJ$R!ik@IG&GC+-Q1|`} zl$i>Rq1Y`Km`?(|nZT0ZAx?5AbfH@BaIMq4yX$40ho^@ri_7+VV`F1Qq_eUUl1V-A zX5@b~GBS!pLN1`lf1pzAeBjsn8%N&XT9|WrO$d>_<`ll&AnLKe7dX@ zHGa6zt*_`X1;PiHCt#Su#>UO$@MMeHAHnQT?ygKoj6?XGfI&bKpPU{d)swgt2+L0* zfSSnFpilF%8JK@#BaCGKe0`6L>q0``eOVJf006#zbU*0!C$bGS$nq`ba-r0d1biPZ z^5XNz$5`%Xc1KeIz)HaNh{F!yJ!J1XMEQpsEuzfosZ{RD&onh;ER>Y(jx%PY`lQVA zmX<%lF)3waWC5*e>b5{Dk8ojTN>NEE6yteoR~8adwy{a=Se}SGZ}S_k&+>G=r=+B$ zl}vd}EKBv6LT}J`nMO@cMTIqRv_~XY5CZSs^PdJrGjozq`Y$9!A$!hyvt@_igsC+s zE(QL5InNz&a&d9hv9e-#=B?z62palwJIgugo-9i6ma zDmMibJx9)3%8Qhd5!wUw#x8xW3BXb1^8-!^d>x46t7O?$7DApVIk1bL;g(~*#l)Ow z7T-aIN(n6npuq`X#V1!FPYG>CZ!oj5i5knzh1nPCOR=&%*6DKj^}D~*y2TDSih65m z-^q82dH+X%w}Sr9^Js*H7Gl`z`2sV1geYubaB#3;f8uwyxxJm8o~lbyVQXRB8%RG0 z7+6ubSPg%J+nCe#^6Ga;D5R`L{g1Z}bYO>@0<7p>H6c2)C0BbE(r`iWg*;yAUhfTp zZ~G?vl*8}YN7Dbp^J<4RxC;0K1DXyFkB^Uz04?ZGc0oZ&SlC%ARmAV#zk3aG^)?rK zdi;$f;lY4CIk==Hny)Ux%a2T=FRc_$a!_4lYi1XpnaIFE^K^2kqR;~bu2lAX@X^!Qyvk~z9J=_v`?415&qhWWl3{58?V)wNBDMDY>OL4&z5h=`tk%@z5C#avV_F2!HGE9xFQ zjtWO<@w-hRz)~l{BmrnMxAU{#%D&QunCK?%fTm%?_TU{N5i&LC0BLDS*BVdj$;4!9 zYD#5?>6LUhGyExznvLyhwXLDSd{R0*cD>Vl0RWJAZkcFk%(n)lOBAyFJ@)q=9Y>}N z`SlFJG)1=gYpJ629Z1=Kf;Yhlp9Q}8H@rqT9h4sOF2~iC;>JoE6+u>vrD$S(>S{_M zLGLJ;T%Q+Nm6TX5&YC(fvBV_rZ}aXyz5ruVA�Wr&}&I`=`@}M##yZf13mZ=PN$r zA2u~MQ4~;^n(UIz2Zx6C;qF{PA4+BYiUI)qfx#jyZ0xj4mDAj`=lAUIN8)b5kve_6 z1$1Nn^eK2&QBhf1`hc;N$I^fERcs<2Lkj9Z$Kxmf`f(7c~ecz zr%bm~2lhp)HvxF#pE>MiJx2HBf!kW8SRrq`IIE*IP4kQBTc_~RjEqy8JB0ty0@C`7 z`sqy9>AZ&np98~dYg-?9T#ztc0MwDqsovMOD;zT1_t&ac&lxTr9yxj|C4+(56jTar zrT{tF9&mH#r0UmxS(*k0bc`p(h!f!|5awSkZ!6keayGwY6<{LXYca3U>kmZ*b@6}A z8!djZk(2w5ri_!J%-dFe%He?|?ocVIAzFTTcvxOqqsxcQn5L9M%VfuaGVg7b2o6K8 zmW2=lK6%OvbUt>>EYv_2kC4cUhK^Np$m45CZg_NIx}Y7^8an~n!Ea?y%autS-gKqf z1;a6hE$*$}3)ObNCxcA@FQP+q4NwCC5_GC1onbA|#{Z zquX3XkRc3obnNdrfX4?$@bT$Ui_2c&mkA~Qvy7r*C?*NF-E%MWld!?#&0%I@qR+#H zA+j?=8O!9yf5l!G-2YXTHw3~JDDPldk&ME^!b*Z(j-LacWxUKn!{am{1YLH2Qq{t9 z!c}QE3pzNw0vcdz8W+5@G@S)E2o?ccNC5N#!pq~yVIsH?E-G} z&?le1j}m-YyKJtKa}14)A|CiwNqF9n_x2*ZPht1=2#}Q!7x(|3oOD6XUq^WXS=jA! zyayDZcuzG>PH`U4BEriHyA!LY*I8fh6NXk+MmIHLHktD@zZtD4gD8#oj8qg#+=S+< zsj9=sqkDM>$ho0tu758eV=%sezg`8mvsr>z|6~1Dhl$xn$bB+kP;2dSe7T8o!TEK( zfCer{7@;{r-f;RY)(dvU#f{zB+1b$Ywqt2^7=x?7S2v9U$dvUp%aU%)he91_DT=`H z?xKa9esdK7iGxA|t&u+bsCT+@WM$2bx-cV;-XtQnt>SaHbH`bvzKRYK1HdP+Pdwfo zB4e_HYc)FqwmtQ`cde~y`l=(=OXZyHUFa4Tl9G=A1P+pb-`(%m5{hJd1~I>yrW%?- znojlq?$0+A>pj#3x)1u)!1dJ?1qCIbT}4LdDk_pOQK{)EDfKXX{3N?Q|0vPk&$`+H zg3KeMqxdr`bIBkfhIhpZlS;YN;qlr6Og+`?2WSa5NL^h(hG zl8c+Pw%(sD|8mA<7{yIazHo^^LJE9xuDkYwett28v=Gx)4|sLJvza~Nb%t>l5NiXa zGwYq+8d}nb^*C%|=lg>bEPss{99~9KS>(U5ZUZ{KQ@s-rcV$gUUT-?F^pH|86fPDa z(h`a;Obx`RZ|Frx?&zqfVRT|%$6U4>vJLpbU(oYV;rQuTG3O~6Ibh(vZ1e!CT*yf= zo*4?bR)P!XZ}qCGwDW&CT`Xea&VNB&-F9Cl4CzDQt_I)*od8`~l425KjGNYYcWIPEu>B-gNZh0q7-$hMQvrYW37gL6FQLE!b~yV2+WnZB0xV6&1gQco+>J{DqrJ zTO1`gq&A!^_+vjBWG^7WDZ=9Ef2EUi-z*;o+ zYcV{u_x1d^%yQrqU$^kXo~keK(!p7jAehvXitLcxuzYUDQ6;D2wcIUNTjf+!Q|sY& zz#mZqDBX!P-pKr*YyzuwJCD8&s4Hjh;andFfDp*dmlX*Vgz? zIsBphaLnPdgW|&%vNGB&c1*1_ANu~1eI8G*&A}i;N$1Sn{)XE)Bl_p@k?TD>{>=f; z$jHyVUCMm@o_+kHq9QSazy_1tSI=I2k68p&;EdTn&Ey`r_8xY(EVIllcTkm(5Etf7 zW%&#zRHalf^i}%n`PzrRkjR*?c(|BNR<JhX?5H4RYjb`-1d6cvJVbnhvRLh`rDX*)yCP>7#$V0 zI6#<@;X#MM2qOP|MH{w(y@AP2l(oz{ty4v^sSK&i0M+&_+eljYqJ`CRItk9K81@gp zJqwGf5CEiLnXN;^dcxvzs!hOv`Aw8ARA+AaXL)1e*z!-Lxxc`@EXvR!%xDMDSUEWt zX4j1;vTCBiAG~gO7(-vIpQKfaHMO<3N)We)9e*hP06j*R$BmXV(MLoz+I9K5Pi_Fs z@a4~ushk|6TzX6lb5i{ALJiP-(BJYb7F#d0yScGV*T=pk{byV>liwUo5-68Eh8Y+h ztW(*a>Y^ROl>amt9udXC#)T)BlAey3T`D!H_r7549Y1R@28=q%7@yO_dmCM_tZc3h z4Gq!Kj_`Ff+b<{Oz{RwW`lctK*#2G}>j2d?IGtzS-yW%NK`7qwW%(Aomdy)GRu-q6 zuvE#h6CdwtP7DuzuL^+}Z+$&DChWJGjW3UkgfHCPRTd9HO_RvDj_!>EIBC1npQ{~# zr}CJU-Rr|$Z?D_4CZnZ#+zRzxM&9UKK(=-Sy6ycMk_-t_-`K zE~^ZLV?Ij=`}*8m#Df+jkgl3aLWzz#=#-Hd1PU2Gi!M?_VoDC0VTf&7T(^9bJQ}GW zzx$bvVzGQC7cz5m4-}lVysqu~-l0Cw1`OX?)JuCYQx{_`368WWRuF_Sv$x5GiWhT9K_0X`RoCM!<$s{555Kbku&Wm5#a=f!bP)zH6k{AeJ#87y*}rWUb@R z!ZGlzi%;L7iQ-szfoho0kJ)h;;K``hGNG~g0FXLVRDaVg264ScP5TsO8?@&=u=G5lM z1{~-_&^F#MYIlHyo?Sm+OD$Atc|J_#{XyP~jPxT8hmk*eoT~tFz@WW))9aMK&~QCO zhUP;7>~)cRNy#boSwqV|Gx+K%E8i+P-o42{XwPHY+6T;~OfGv~zq87&F5fvrobaN1 zuzfXl+;kRD&KBi~g^C0vCMM&`&45hh9Qby$5ehzM8fsk__Ry6mWJVa{;?8wEf7wST z?Fmb(YPAWr3$E;IpfA9;!81X-$JfT_>43>8r!9PsA%HOe zvXT|=`_KO=M|IBTW_WT`E~Q9brg*H+pH)?ZTa9ewT!4)l@HwCENg+HZ#>bn$ZdPr)Pt-fQjN_%YwBPEXSt%ob&_VkRrB=MpL$V=;$oLI!23;mv8VQT-eyq zq+4qMslaUn?$Jzk9|=3`rldZhe+th1dM48P*SS9CO%>Y7*jlPf9CI? z-XqKW{7L-5CD@PFqcN;TMM$dCPJ>C|YONCD%KP!E6b;gN#W0P?~0M zoVW}O3`}rv-XQQo41?T*hlzR*g=EwQ3nZt_LpWyRSC7A|-{n`pT%jb#uTnr2j1O)~@LqAi?rTOdkwqMwP zv=}*>t5`ZUjl**6&F||2$#QK?&D=+1G_0_kF7rtefI|iBv~%M1{hdAV5@*Tkt~EJS z?tP-5MKA{Ve!M%%YPvt!umofd0JT_m4NvFAVG7+q|D>`_usDRshK);LXl1r{yxrh5 zXT>}p0(Z8(tvj|qJTx?4^|>aUspHei zfqt3$qu=mDNSNY4m;r~YS-5|G%*5QhvViV~2Oh+s&p}Z}Ha2pT-70FZ+cnR9w_qCH zthhOulRa>Ofzzne@iBN6T;Xdj7@ktm&UGjI7#?K1(gN1F%=FBDAzt6-#fFkk05Y&p z;O)-bL(5D@$3O|a28Z9+k&yGC95H32uC5Yr8Ye6Bz@pz3?APSmFG)vNMyFC@yWVxW z)L{O*-4$L}#H6dX_97?|yCI0ZQ%X}#4yC3RqstVqj3=0Oa7AgXmJ&z`(AJF)uK;(_ zh6EH4Ze&T3l76EGiBm@N4S&etPjB_RoTxyH)AC}W z6fQ0=gOd#z6v2iIJaqm4XaS_c723(TkD;M&iJ)6=i9s90=UXa@#EWTA&lKjD&bCye z@7L-8E@5Jg|A5fi+S(Vdz<|!``wTOZL>9BAx8~3Qk49)>VgpdM}_Q$ ziAP69=F2p~%Fz_3^t!xQ9jC?Z!og6==fbUEcrsly3i4Va7O?oQ!1G76af$Rz(3j2P zf~?ssFS_H)-?!g!b-Hpla^t29R6Vd&X3`E0`Y7}Ocf_C_*iCcAbDg%%fmP9gBpM~k zpnefq6fK9}6zL=(&Yy~!dSc#ONND|dq1x2UOk^PrBh_G7SpFMVlPrGbB{ucV(HuM+ zIrWF;t};ZM1-@Z-xX?!j*l&V9P%7`g4D5HJEp;Ag?dRjAKkp&EnW@S2t@pTvkcfCaB8Hz$b&8@mY>&0YVjv7UB4 znf+t!iHEn=?QY*2C=s2>A?SLv11OjOg!{e#U85GZLT9R%B~#qDKYxnIDF0kvM@M_l z!9pqKl?JNDrn@@5FY01?a=(I7r3P~avCe;pBtcQzFONH4!TmhAaPh#Rb^d@%vF}2A zDUaaepPe7ZUag6Z9DUFrvNbf<(Fv-RKtUe);(PnEFEG&R)!LqqJ9H)}TwE73C9|`t z(q)rvA=?>=i49hBKV{rf!1Iu|9fx0lS5;K;9zLmik$E;9+^5E~SW$ry7&k4`&#mt( zCgp?d#v8Q4wOCRi&}vKnli{K03qiU)Jqe7_tg~DG>a69AaMqNA!doG|7%Oe z#W8Sw=PT2JUnXndTWYHJ*1+W-WPB&3532IpwrT!E#vHdMXBs-ph=>qDDV)?>b; z6yjAse~l3k$6-7m&D50B$zs9-W@byeL!dHgwOCS;lU3keT(!F$TiM#8B5j^-KOJmB ztKq5v+LM?VrG*}8mJo-dtQZBu7wLVf}(`(Lm%B~{k%y1YU{*XXv>voHub-JsuP z{ii$EfV_`kq!0nY7YF~V%kTnKU%>K}A-+A!yuj2*W=A#g>kqY0K zK}*8=OlTV9%g@0hqNB63=HX}cndbQ|ExH)q`?Ju~gFj0R!{%m-px6jtRcxDrj!Juy zCuW*B#5z@_`s-=Lj6;3kf-Kd%k;&WTjH6v02< zDJ7B42(*LJLvpIiil`nvv!Ibao6H{k64jOi;74F`O~N0Fn|`JIBxqnmPQms_=2XWUfQCy%G@7g@D%x=kZ|*Tv3u-tU zqhcfHs@QfDjr7hs+ugxyZqN<;CU<#%l5AB&9@FDjwsTVu>B`_uh#7=#R+C@gPrZ?1dU!)1M&>S6EmyXu&MrLafE>S=!0- z&n6R_BLG{2vimk#x0c!&5XWG|>j*2=n;>>8?2Y=ecR|dFy$^NG1iNRdt^yvxEJ&lr z7_NYQHnNTvr=Tv#i`-_&(i5a$fFF2Q0Y(Y*t-gY@!dO@T7XxksTDFGz1~zu`_~XOF z0ET+F`O#7A!gz1DwEBSoaiRdzDDEo59arFwXZ^Uk{I$JO~) ztHb$q2)^!wH_U;rSpT^7HU4ts@5mm8$y?XvCqFv7vb0_@qeT8e+E4Me^P_}|ILpT_{sv1V54JWJAz&wt;6C{3vp}1 zD>MuYk&myztE;R36H$?tM!X$W0z-$6j@*lCQqslD^mK6703c}(A^0X{Wkt2CG=;Xe zdy9(l-{Qyr@78Ptr-#lPz%l@(+p|It{5CR9ND>!(0h;B0P)kd^U4mU8JEAuXq^#Lj zlr)w3p5l^{_SWySSztv%{84(u2Hha`f}5MZzqdEb<_Pc;e0leLFur zmB)O>dS;VNPE0&KdEN~B!E?8Dw52n$t<>cItwx_o<*&e(=L2o$CKeW!*w|R(*j}Xk z3I<_PQqnt!?t0jViC>s^BD7j__J0xmDG}=jz9A2{*k4~cRrs=~hW3hxsLdoLCsR>V zDK|-~s?M&TVS)wvuWPsp49u(0#ldF3+v!S0SyQj0oBG|a0;#arH+BXV;0K;yCKVM8 zl!_$~WBpwMiZgEYhe3_w-0Un6KgNCKCO>vtn{&G?P62TOkSv$=X(%ZACMRn;Dk~cs z3u{YDW%rpca>3Q0ckasRO8avYS4*vd3UD)j(@+%t8g5cp1T6BP)fc>t@vkE#XyZ@$ z+-CRmHk1Vg*IEVbG?bNp?q7py>}AcCxv5zUxxJ&U$LGQ8Kgf7w1k?U#=@}VYPst4G zI;|ek;L<36xAn(ajkD>W@tVhv#QyCc7W%!^M{ z7W=n_g{E@ya=`1eWS=A8XJ>2s8(4>XO#l`Vxa$G6;^O|^+0AXJ#{TVF5Dkrrif-fh zCJ6gaZsf3sj5t5r(^H>F5hnocNmEm`@+)pQ@+UHv9l$9mA`$yW@F93z|Z+^ z6zDNoAACY79&Cjf=RUzl>@L<(f-_8hshR4jHtL`S0Jb^h=jVfG|NY5{ffgaB9SUXW zUO~0Gl!RKyV`Qt>EmG8E7Oyi{k^j=YuLT~9by>u+<9`HY1aKF_f0UM#+}j$wrlG*%Cz9l$5z$(uxWx%x72dt-pQP+zI-D@hdvJ8HLS9fMwMt*;hEmZfL`Mk!$D1BueO-&Pn z+}LB4CP@dy3m`}XT}RC7-fa8{ApW zfD(oW>K*)g{1?z_4oSsnd*>VdUjOns@4aEafmo)nCau=(oCZn-p&k-4&fpwF>eJ0( zWKwt$h+$fgKVzjy`&wG2*Fo%hJRjr*1Q>$X959A4(2;8mDI$^7OpS~_wWPCL&UI#sw$Qo0V%%s=GI^|5x3*|$!e1`(5W6D;R1hP-0CzL z4bE9FfU8Fk6tt|2?8O==x&YV*aO{xnO?sGX^?eC`;Gv-@F-sYzg#tKUjDK&nb7&th zqJIhs5)($7Dhhzh=6C%9d}!cM#9ggV#OhSo+6t5Ua5X6F^LSgOQWBF@nFZ>H2j5qD zI`6*~a;)dN7p2L_$jArDt^{_N`<|4e`KqTs3;7fdP$08_0PmpWU^<0kA^Ctf;jicy z#j(L!US2-?3dheD919T9FYk*AlW=4+&(@lra4;0r6+mgrXJ_X~iAX%>A7p3X)-`j5 zvkm9pv$MA&GdjYk(;6qATU3;skOhD$?2d?Ea|FCLJY|2%fEr81!q&?CdI}nYuU{iT z5h}ihx5w+>fv<@Q-voJ04!{>%6+lZ>Pa*EvSh!hexTREaJn;wpT9H4ND6*Ambb8-* zeK|DJv;e|Q3Rr+KY8{yM1TLCj-cc15S$IYC96rM$Z?;m?(ERRj6g#j|JOIL~$Hj(W z5;}JyfUz&h78gps+)lzThUk~6T5Th&rP*ot5gS6!S z;bC5RF_O>N(2(TCCE{P*XF^%n$-Mttm2YU$tp3HYqIW?)1)u=xOA94Y?uaOwx;eVm zS=mw@G|xeZWa4d{*VyN>>|7&k{<$uZ2nn&EJQ-eYB-{W*9B>k=#oaR6+TF#iu4X>7 zvwIJ0ajaNa3GiqnCW#`qp_BufP=s-_1PzKGz{5cV|ATGtXZ@YyQ zhE7bsv0Fg%;mZLTPLU%vwP35C-`ew^?_S@*L0YHYq%7Cj`(@cRO_A7E_rb%1mfblL z78wsgw>BqqXt~K60DBmEfTXKME1PbzU5 zxmIBVg#!R*a(BEys+@fdUy{3FFpJuO07}3WdPMO(cP_{gY{3x(+*~EZ^E&dOSd=b4 zTq0g_*XTH5p!276@)6@#fwF3uxx2K(JK~!tfPgM<-fL@ zLTFq>^^w*5JjL=3Ojo^FNlX;lUub@>8%1$dn8!~Nrmz`*fGm`Y2<}&| z08oob#}y6olE4=LS~?QyIJJd^aS`fC4xq*X44t3ELQxQr(ZkxF^88_uXIiBbfyXJk+W`}Xg^5Xk zkEVo7XcE+(;#h(;Wi`66uyCo~>F-8|=cw5#7GD3#3IhdO)+0lYD@PLtpw_Fd55Qnb z-F~oQ{q^h0$*GpSl*H4sZ6n%7Moa5Y;Q-9to7qN{l8Hw7>b}o$3_6MVMPrp9tk?DO zC>7P3E?jf##g zrGOL}(IYNW-gAnBv!lVNFX(>8-#Y*%9kRe9*I5x|t#Mio#<9cSe9R(VUKvr9jJg&S z6uBZ7Q-oYrK#q`S4G@V*h46ax!cY+&0nSUHLf6(>9Bl7Gn?u`2lye3{RjF-mZ&Ok- z{RU`y;ForwZ2qX<`Na}oqf8`IMz^wolaHLNhm?wpni=hCgLz8c>pq#>pvUo0FbVhGiFK2sWP(C477B8m%zFpS~oB}EH;?= z!IrSXSk(04qPNWbToCN$U4nwdAKU`N^nbFP&Q)-CS+2ISdUNvf{s99}AF6_awB2+e z>)J>gAW{*g1-t?Rq2Fe{1diB=@Pd)C(W>BgkeVfDr$n|FR}#5AB?_z7wY_+x4P1xr z9#U>=Z=>{xX;!SU>l_#;1Y&-jF@;soY*Nzw`PwTB6kbOXaEVYLj#mYny5r45DVi|26g@ILVIf$U@iaW-fV{wFku zQS@Hc8B4*Vz3I{I&^TWMD1d&xz*O(sqhw+}ceTtgYmJ(|hs^Z!O&0=!!;(*={j0sZ zfwV#Tu&|ZvC7(83Co|;D-wc_r%rk-D^VU!ZBv?*6&CU0Z_pNPB<>$gzSF=Ex-1V5d zhcm)Y%5dr|=_jsY(L0bLPGk|A@@ zFbXmcR!3GKFK*U-!uCsN4aVFj8k0DFt|1iuS-&C3?)!jyw+o9Umi?eQ1EaO))n zHz;gu?07~N7JCqJx7h`Vo$oG(TQS6Zqd!N5r}HT&ft_ZGV~zL2*=n2dpDWx|VH;iD zPH1>EdO8LIBBBCUF~=MbQ=K-Ig;i@Kgs6K%cnN^}!mhb`sKi`4fsnK*25d<~eG-xD zU7pZCd!4X?e}ZcRymnuaNHQ8C+Rq>3oeqC>0MZ)xS#l>cX1POtDOMEBcSs;}5zzAo z)8kx$dp}WHCaez-r2j+>SrL^>TGdNJ85i^Yu=Cm?hZWJOUmfV1_vcdA%K-Tr|8KnW3z(fv z0_l4{hrb3uJqO!N_p5*7tm=TXh+br51ZF5M_R}g};k%-r-D4N23Au;(57o0R1;!Zb z0G1OLM)|QB! zjL4z5M7LIA^%1updXrWygwDWFF~bct6H^tl?)Soy63{>me<`i_{Y=11loc{DsHKNG zlxMC{i@k71Ve-egQccB#56m$XUm?vg?#wY8(7A+rjQr^7n6@c)Ml^l(1bL9y5jvxW zx5@3`x?t5I1(C{NO`7&>`l0EVHiLwI_4mlg$~rh93j8G|d>gC5b9HqERas9rm)kPsY`nZnlat*V z-vk4>Q7`U6p*?3sMt~}qBT&X-%ZWEg@pf>*bs3fFU&Xl9;7`N;u!@R`AZN<3yBfWK z(mFaNYm^&QF!6Qzbo6XK3`%T-+o;bSePOm@v1fTO1|yE-|Har_237fnUA)rW(%mJX zbV-ARbT=YMN_R>((%s$N-3@}$-QC@s`#)#i_q-pE;|FKhv)#bn&vV`DTI;tWFc~rV zn%=s4`ayTN*f|NfS^U>x!npt>0}w3*SK;*(8-2l+mL9mQqz&u)#4+xt@^hpQ*jmsG z51{s5A${^MF0;mDeptk0+|n7u)a3&W&Gg$ZkWBBHuJX%Pa703KElL>TAt{HRX=`fY z%ngP9xd={-7INW(gWv6xH_R8zsi>(3BJnLOEJnAmvs80-78h5&E}rqz7@K(iGl(@B zeAWgeAup*L#(h^e@P=+F)9)k)i6RuMp{Q)ozG~lrf~@eB%l4wj)nx^^^X#2o-#x$< zl!0q8*h1o9V>Q;)Y$_cR;sF=Oi$WI&!u4Twe--~CPQ2XtUqLsrx`f*R$U+tr(D=#& zwAs-=T6y4QX#{B(Oh0{KyM1RB9G3uC1}q(7@8PTW1PPQbJq%Q^dqR9lvdz zWWo85bFo=#=uJdUGttwn2LuLo<8(LrhpO8*Tnuf|$_ZioTxc^}yvBcgd<=ID@(d#P zKOZ^+=WTP~ux92B+Z6Fo3{Sm(cz_VN?dQroP>Xdvy5GYjAaXn3e$aE!atmy4|Lo?Z@FAELP&NVk`%6LZuQUPI8f6ng z$qkx<0x-0Q9tGlSR@uNQ zwGHk$f$&6PIT(~*a%zAWNmhEjFL<$tsw(keq^Bpxdd*A0!%C;K!Tg4R-`d7zGEYhw znzoEQGtzo>(cviYxoZf}LBKg*LrH{)yd4~!Yas-acFwC5#A+Y!&NB5C4BYon#m+}Y z5bI=v=1@)jj_+S!f9!$*v0E5GfbX^w&!HHeg&Dw^ysrU+!|^7C?Rev0W9J}5&97A` ze&7U@2OVsD?J3ZJ#^9?H#W&MbfHz7_cYiwX_5&Y<;^+QmNSC=nxs2oa;@HBVJrGhx z|Kk_U@Ad5RDO1q;beZzY?^;VZT(~QhD0S17#Y4cTH(mL!)na61wfQ?=kv8ZWT%cwF z8q1-XY1aS+9u@&+T0#n{^!-40_fV6Urw=MpespYnEhy*Ic?M%CI0arXcvaO>R^VfUm%{W~OEJG&G%XXc^FUl35gi3FfYpeQ=rZc@NfchK2dK8?G- z`2t-ST|6;9T2@L5_DR@|Yer3`9k7TGQFNA7qJWNc<;5l~QA1jL-=)%j(fXsD>pPJO>@~R@Q?+w6O zWq+^FH$;QxZVj++U}jm0Acm?kJ2S)Uz8V-rB_`57`vjkV6gvPeZz#K~u4Jvc`?kn; zRJ1PCzIzGhGN22tf&zlsP-jg{MFly$);3o{Z*^7SNC3tQGG9_o+G^qO%CU=FNI*nX z)TVMfFAv;bg{v9pcSc8txh$vegATE~80m(_N3jaGo^pW4@YB>MunO<}r0^h~^{{?T z!a)Vhmi*Wo3Szh$0R?$^DshQyeoz83{=k<20s|otAqlbZvNAGITrC#Pxbct~e?X>b zGiMVZTJn=Z6XVh-7%8vzr$mRWKy3p!T`FQIK4V5X3H(kJG#aWRSY{(8)(sn|>%1Oj zc^6Llm&6KAorjA>U00SEKk*^ww0lmg#u6VDWEj7F|L*686pdCv$U4>>Y2Zl5_^}ND zF>+>$i;5qg;JWPL5l}6cn-I7@%4%yrO^3vS5wakW7y~;#Do$+1(A(GJ3v`+H`_AnA z5+jMSDh^UdgzD3xB1so%)AzyO-J4oPckx(VOw(H&EA*O?p&)w$;uO@uk24~r&HZdh zj;!K&XVy-gi?D}?`aGDpyvOGLJ^xIruaU)y#?C?l1X2DzyrYBWcx*Q;*^ky#9 znNix+JFmSqHO+U)`v4+LUJ}A+)Y7~w*UVzwcq*q{5;wx*`^^)OQIhbQbOKhe?((Eq zbkfSQvJbR(%|_soyk2>GJcGpPv^}Hs|I}R?*^lAv#r+Z zYq8%~8AxSGDGEZ4U|Dn0a0Zrh9?J;2pI!q74=_caPamIiy<>K6y*Cfl`v?mUA;gVm_XcV=npqecQczN^ zNq3s{ubKifBZOO1he^N~0127WO1X>mrebFtrZUjnEN}Z3?Jk)}j z89y7H|L6rjeMGGq911k+%73C#(z#(K>=KBN{fmd z?kaQv$BU9XF@9quaq7r)!y7%Xr^i=BkNld_bOfB=*4e<3UD!w1MHWy?f`$X8)03j! zxklEjtXrZpb6KrkU(PUUbau&Sg0@7&D*^Z0F#v?Jq1>fg^x!bUMeRkw5dY=a?s=r9 zX@=i72!70elG)IVgF`Ve2|~RN4{B+R^LG?r*;v^sbvnY8vqEuk&L^X1eiWmCL3Rz| zzd@D~qM+ZZ+1HOWrrg8rlUDg48mW&`o2^R(+1vlya|v)FxY)$h^vv}8y?#cMGCzYu ze9|fF=8NrVC1rqnS8z-T&dpBkF@`!!Si$Z3*vw2yo)bcsd5C4c!IO|XDBsw=`&tC5 zZ2zXJBfgY@k)hB0y)C+|?`f^WK@B*Q?riV$ti6+Z+&>Hzm_K-FEtDsb&tO$m72oN8 zc~W6|SMiJD=Jryr#(^Gk8ps55Y~a|JSq+LyLSkt~89inS_v86SJMm`%-sOgOjZlsa zMY2a#O~*|irxAIKjZ7R)S1T_${S>Fekn-VhMmGc16b|if1WejfN76zYz)}O7eK23l zrixUx1!;%x&C1Kb%fy8JN~=QdGO^6ZVT6u}2~CRGrlzZl(lL9D&qiM4!+>aI1vLwU zNP>wu64~>PR0O7|Q1J|`2d>ZAVK)_0sroijg%6M_wFY7tgQsdb>Rn#(!uZjRY^8Z#Mg8f6*5UALYlngdWNElL9QX3f?0(^uIl|njg zIWh1WZ&Y5QGB90q!hye_){mbdNKk|MQa*|~-RFEq`wdFk4_=;}O~%p!0^glQV^^(* zqY(0@4T5AuZsR~HXM1+_Cx6;)=*y^LO(=FzGn}~=gj^dSP+4m-?jH?8k%Ins z?~z`AOe8e`LzR=De^itKJ38lQ0`V2MYXaZ3S~CFaYUZhfY4Uhb(z7}R@S4~dOi+(n zRFX5o{87DY3!#O#-jc)8P)e#8K)oo89UQLxp6-Z_Pq1P!LG#5+yKlKul7ySvBdD9o z^4M@0iXy+3d5`gl{bSlkGfd0ib%nUq_9qa^#`of`eKx?aO;F<@vR00TEyd>v@e8kW zP_!k#p`4^#%*(qV@$~duth0bqhN*7YXC3ZEbA@U(+ES(K2Ywv7ViZ z#33L+;x?3oHb#XVEErI>fRqIl#yE8zg zH)98*D|W4cKBD-zI6lY2K4x}=hf2nO^j$wn;J=#rAAF~zpi_Ad$&iF9uP&=ve@VI| zfAYK6+O)Vs3__eRDf!>G)DdYIi9ldL6+YUH0VJ{F8Ab1-^tMZj2Lpput3_4u7-P zN!JyK`}e&fdw`;)ptMk3X)LJS92l7!Pgm#%9Q*@eMR~w-Dbl$1hXR5vaS}{YS<}46 z14|A+^U=W$LOwEUtZz;XcQ4gca1^ZhA{yLf>8JbkZK^*-p_Bbt2SQYS^Uzup^fxy@ zHyvq(6=lBqi@;Ml?T(qXX?lHn>xNn+%N5S4`=*Df2Y)t2Jo{16SA&Ge6*nwsqDM%F=tnzc^l@7u=`;4^H&vWS})L8$J$YTepR ze;|rA_2K^4sy}p4R@^!FdKxea4klJ1SiW9-{_NaTqa!UMEhn)&QW8;=1aCJOK9qY` zsh~tL3S5rQpZ=Sey-BkKj%Kaj9V|3|hf|?KLz9PlntOnGP#~JDR#npHK%OK(Pkenj zxRE{o+`<{UArCf0N3*%^AfI5-g!H($K`C*&zknpLF^o0X>Tuwzz$yMa;*VB~C4Otv z?JVGfu=>0v@xfANo9^uqLLlkS@*^Us?@yH$cKG--G;reaqXOu5P0jw#^6sT5h=i61 zJdTk+Z*aCh@;V(3AcI%H(qJM@0EQ$gIvV1SIT*~$4$LmJx4?zD;cR~R@?BKSWesch zKVfI1ybeSzNmy)hgk|oPmZ33D#3NJF`sMMn=X55ndw-ZJ3tBP@p+D zTs~M`MG5S?T0=_^No%C{d;Khq_H8KNH1FuU)=0~OZ$BO|HSP1To~L0r>^NbU zL68lqsm;RgIn^HkrT`;AoGK+nC;PN&Yic98)n0E89?j9nW)R_H@Hnrw-8VJe5YGNd z;rO!ed#v^j=vI*AflYx5CK?)a=9H~h>_?$no50A17#d1v?)lkMECzcUn@VVKl#yEy zO5EpBUY_$=@OA>SBJ?vIc+YFLTog8l-KklOV zzJWBc@Y==Ajd&$U(105ymu%eO@iG6mO{(u$Sgx+FfMw#4KNNiah;k@Ox_5_xho@2H zu{97O;PtH0D);jzF|oeB;Ia?ts{6Ih*_)I-QX+4A|P*X=7!SSq0RK!nkYnSabv$y`&mU>&meJN0rjVP}*HJHA88YNq06UVb@>Hrx%I^dkrc0Wjz1 zc78ycmp$4Ed2Qgl1CkL!z2{*Zy^W=T9$AiVi_N!*w&sZO@rRq>sSjZ|%pfurbfr+K z=jnBccr=>m;J_%vYNZ>4L+>=p^I$SQ!pm&|MVT9&+u@{Px^@Sk3B8aE9&Tqq=HIm^*&f9h(WM1U|hS9uk-#Xvzu z2G`bpmiy#$xmr67**5@NUv|*_m)*8?NGj+wJyg`t64(+D5YT&N0_Z<~|4xb63pas~ z0#PT|^1FLrrv(Nb@(I*K{xXP|0^3j3FL203jGQ0dJD2Sh%1c&Devp?!7klu5{;;V( zJee&TuyIO_6R3T4eV|O$6h&X65kf2*0T1VeuxIJ1%*w{dNJjQ+?MEU0MCKZAm~5ao zN)b4(fenAV`3*yeBNV=s)&kItl9N-)b(#%9d|mQvPw@?(TSMPgx_+f%TVZS=_@5?!%BP5swwPG>cOJQ0N#6H>E+lQ=x zcYqCGw@K#b2q!Q#f`FiAo6E*6aqC1TUy#vLmT+enZeZfo>XxU91AA-Tdp>VfR2}b- zWY!JV|D@qp;XUSbKR@5~4_!=#mxMUozpL(v`Nf>X_*plWhg|wdq}Lu@&rjOg+xQ%g z6>J=!a+)|w)zH9JRX##`rzYsv5(XCW&-x!Xk?F1Xft!K&QpqXRa@pD0-#%N3Fxy`Z zz_DqZv9p_ z7<_{vXl~~e+}$sVs7%;nFm~&J{AM{<@iN=l(3P<+I)pM5E4pH^#ymaqUyng$hV#+v z%Yw2yU`)N=T^$?W$^9oXN=el2b>fyU&0DSVziVMNek{ zbiyAyGTTwk5B4l01sAHD(G!Uz=(Kx3w(I>ODe#EakzUci2-X>!TaX$h#8-@D6nP$H5`$l{k;gao1FEMeVpx_47p?-UtCfTT~to)N3 zgw~&^atDf_%KZ)h1M&4Wfo8Ra&sH zOsS2$AlGLVP6C2QfFcXz-=0IP{%L&P#r5vvuhDG;6ad^9Z?d7jO#TIxSR&8u5|EF& zfo&ZB`Y9_Ag<#!|^3|Vwhx8Mg<Q ze*V=?6O)(57SEh)SXg8iR1Ih~rnLL4aW9S+PWc+A87-TG~cImE?{=~yItPF5P< z4rds1>aKs2kUrj?{b6|+lv~U%``aNJ1X`$tm;~ImKc;-3|3uGELA1g^$%f@c(8mf4 zLHCvly@?<6`fTiDo12>UkJNpxY=B{&6_TMM!hj&-pG?8Ie3>~Q6C*NOU_mRcl0k&stM_y|Dvk7* zmmJy{^VXf&ygs`!F*Ang1M_oq|bb)`GY7u%Mc~-KDS8H#|ivFM(#qBhJA{62K*5W|ahE zF&4u@me-51Od^E?KFHpU4bI<^8eN;fZ-dUzqjlJ>8M2%_a&F zs%Y-wOo?~84YU`7RuhR~yw2{{AliIkS(=Wa$7S5dY=e&gU(V$WOBnxoMUqRnLy*1h`GK4Kd3URsHnWOmqlv8#)`e7{+>I)@Q?_X z@O!giKz#fRAS0)z5j~)>v9W>UJ)^9Udw(+iDJWN#sXvrIX)mYowzL2us01QtJ)W}t(urAnbw`oBUsxztxU2RL{fkcHB1b&`>kY;ruJ5!_N* zayqHd?}X(D)jyTpmkvk>@&KiWzn*UaOsmTFutesRllS&s| zB_Nb_0f}>pwQu3Fx%G;Gh=>f9s=eh|kot6#jP?LRt-zGXtbMzec?64!*Y`U$FtYbg z;O&xiCkmkeQ97=)rfx(Fs9loyW_s$--Dr`caB zFG(zq08lPRmhalXxHvEn=&nxb4)lypG?L}2Z=$u&{xPAUp#*zMU7mX@e8x^Ec(e3k z)xb1B#ABxcEFsmdSZf|@kY-&lVK^1q790j$LLt9Uem?}C!;4Z6~}hK*Uk>PZBPtw5SnL z%GM6oE0cRIon%X=7g0`8y0%`8uj!&9qU&FKosWTBh$|?#9141>#3$h!XQun|0MQ)m zH2r=BxMO>#eEde?@dE12$S!pnEL|lfhqOvLg0ax0dQNr=V^q}?(*-;O?~f;DEL0Mk zYEBj#f#C1oX#FrswqZg=zaZ<^8;V!1`T&0dDTnzr*j)lNof7C617q$Lj6qn7;~54f5%g8R&EIl~ zSF&!A(YwPLq+wS_N0cPu>rGVR4^)w>#rBEHv~oJ_cGxcilarE3?2Q(Q%ulzcQ*)Iy zF2ga4^+!obNg_hlGlz{o#gZAn^2++;_1L_g`@nBNWb?}%5vx(s{gTx8!EoI= z(~ZL4qz{RRGXE@GkS|Tb64g~EHw#uUxIFJeJH3Tc=u{KZPsQb=H1qw$za(+%aGT+a z1KZiy7_yi7w}uSq(3Xc1#0Ex{qM`S&%#Ee3+8!eOrF$mEl+!`WEU#)Dd|8)WuLxMQ zrXlSVgYSP*0dd8^rMh>1Uc*ldb~(>I+eL0e{eqWFuESv?=6B|wO=Fphq{*2n&(~vm zLB4Q0j+ur25Zo#9W=^fTL}^Y zUL;I2wB#PJX!_u}J?+Xa?>-06l}tcBr~PVDefD_S+}zsP>5y0{+6F7#`=8goEU)?! z;Y1;O!Q~MVwYAD8ysj)hYUN7Uf<29G0`9^NsXP`xfXf|#q+yYX=4)x6LP0;yPiEb= z{o~{L&Nrbzx{#V`6@I{I0j5fz=sRdL@AN&2CgbCyqM}w|w*3l7ZPS`De@^0CPPQph zKzTiScK?aY#gMjv<0!m}ba@f^bBVQvwL-rYhXmubdrLH)L3Ni!qsu0QND^-}u@Z>s zHMOz!R#uS_k*mI=C>0r6pa?jOrQ{HE*M>q|cpF3s08qaQ!qZjTo<*-UN^ zmYmnS+FHSBH%qmY6V&p)u5Uo7+ssX*>$W&(Hh4S$m1%D*BVRv1RQwClYP*j`_Jr&s zD)|A}sZV-m%jcWqX)|jJ7~Ow_|HN(ad=PrGlh-Gv(Sy(SBbx;*1UK*2;O00uCBA6X z{;x|n!UazVpD(O7-IAzQ_^z#rygXiM^6~t$%D2v)ZO{6z(!GT@xA?vn@)EC z(k`eAcBJEy%WVDpJ|`!4?yZ?QwZ2l*rukcMe&63%m%nD_=H$b0gYo3>Ic*g2F+{!S zeZPg4Eif98RwS{gffV`4ScZU4cl0wiI%u61)7U)O39#9}P1mNE8WxFZqq96+Yuq>L z7JPe!f`>ZmM?gaYE#uo|3$+*wE-r0zb92@!1x#tQVxC;qs~TGC94&^w*s@DXdbn^? zD`O4yV}(P}g1Q=NYA`!Jo_gWXKzppQQFDOve6hfTnSiD|Bo_jafHKFQ}F$oxIP!HA5{8kYzoo};3@ z1cy#HMdLACRrPVm29%OK=W$x*8yg{Zc&k)RfMkRi(L^KYgFGD)={-wKOvG9x@(-qI zn}+&^blQP{8hd7uL&H_E_N!o_L05Z7J3wwC6di-vvHMRn83$bzNq%l@Zj5it$GBs4 zGESPW!ixFqtDJm7tV{tPW;&4&h&A8&Xs?Aqq-3F!m;K#TtnCwzuo5yyyESC{V2534u)3 zDSCkusCu`9BPvF%8W)ejBvbYklG6L>_CJ~bUR34aMtLJkg&-WE56ZwMraz*4{1Y@z;e%wkjwZIjyEVJnM~ z4_^Pjtm-4O{ey$ll;q1l*VEI}0N`A7?g6R<{{UEe=Onv!isbdhKU$y;9b3_5UvP1yTguF6dZBnd%uH2#lYyab+sa*f;=%_K5H@LwP z&D_BAzZA=`P3%2UrJK6+A^ibVUD%!8ZzR`b0#|p_t0Xv3x~bYUiUpIlarxZ~{hATF z?kyy57B-R}t$u^TqAPohoE{L|^L?R5q@_FH;fbR8#%OTe;xrnYX+X4TxCJUP2Wc^F zG{M4qsl`#-N{i{0v{^8n#E3-Gf3&jZLtIH(LT{=a7sM&BX8topwX2)08((D=U(5Ni$uwKV_xUy#A) z=!in%{O@Z3|M>s;GQ7xvj~+ze?tkLqCW1GUNLWBnkcqLeA*F!8sb3J>6`GBmhoXni z7c^2D8k+aYUE||SoSZnSkM|pX_j{U}nqXNt-_;3}r6KT@AGE-*^i}Hq;Q^2r5;Trq zTAk@Mr}e?5BK7GmIY#KJ!}~2`EfR>YN3RWim02(zo}SbK;i2K6R94Q(so`cvrn_6X z|EzoTAJ(9uv=0z(xuZqi{6DwZdiFk(fr5#LF!+o`!Tw5Q;kfwx%ZF#pO)QOH?9ssw za7`^O1O5HDH|p2I*(N+4F3~A5N>yJ$L_k`)=G@{}r=YSLaERercYxF$qE!GuMI2^FvN>++{pa>?F!GBT#<{d8>UV~3vSph)-;Hh0?&rOp4 zMkgffYWHPt1ab55e4@VSnw?!*YV9)wh(4zsodB8+`^W0o} zUPl#bY-E(tX_K+$%u6M^RLePGa@}EC%-;7uM{ack89_~O*oe!o^Aoz?E`lLh75Dh6P^t_fvN8R@#iJxyl&fX>)-AVzN7Qq9konS0rpX|Y&3RHCv3pm)Et%8Ep<9my%UFtC0b{m(8XI&ubA0FPqeq$Ol z(6J=)qCzyvD`e4cc<=PC9&_Yzy5IS`>sA?atSI0C_b*Vjj3-d~Jm&y99*RMF&tXiIzJ-ST47`PbP=PqI@=k+v^#KB7pv& z*Xe~)P;8(lmKBE#S-l$dkxW7Tk?fJ%{AJr`OQ&BWz8mYu~f4y6q%bKD!gkYdA zF0GFmepBE7gD83@tV`QUv#0xo;cYXhm&^wa=S)F+r&ZsF%S*(~ezM#yw)6Y-i~WmS zFl}-pBH?`K+FR60Nnlh<*cI~efdKd7A5HAh6%|%C3O2DcyUB)^C}8Sw?|sY9P9?T%@2g<op%) zzVvuUsvzDe4cxdOnQPoW5|BW|>R>8nqyi$0z9QtUf2aQ@BwT{X=yHcH9~2anx;j^# zNh8Th|7e$Xi{}xiA%9&`b~c1VX$prq4Gpboxe`pII?!3nXG{8Ry7$`Moz*L=dxC(9 z-E9VJ73qBLyWo=u$alE6fyek_6w`30(_pv1*5;XT4Zf z+^4_R?rm*uUQm|PW7EqE2?cpynhA2}jHxD%;8yT?71%Iaf%{hKp5nEntc$kCW3{X&MznT%}9{gF|dBIzIaQ{W}iTyxd&M zI3CwcmsTg@y1M#rbfCaGQ~(o)pavND2OP+q`7HapVIXDM0fj?nq=kgfgy1@V5 zzpqb>THIJruoDoA8%(Dlg>$2`r@%T};mbx=pJiKmFmOw{h*dPylxXEMwB%X(xbuM< z&CkRq(kC*ek27%nS&CX+z33@(@`*{oIPftOGrV)R`j++SIZKoY`Eh{_5qmnxO~0BgMn?Hsmz2$x7KGFiG!CzkY=H2-)4;4a^iE zvLpRxNazZ9pR=0PDjP}^sv$d83mlJTO4X%gWV$oJfgOhS{9vna(b2rLv=|?c02S}! z$IAZq-`>ru`N9Q!2lZ0&DNv4#5h^Gq2d^)!tbbXnEWr@uB%i_la}jx0S2|R9BwMTg zP-GZ*WkKFc^o#o3)xR$wG{`S}&g7yq^w-R^BrKJ$ATJ0$&o$}#`1k{f9+!U!x8;0r zBo2;9HO$rC7XA6)Sn{LWENIdATFDfoZAlJCD?mo9zfX)wvkjhAIw+Djzz|gF z_16-4#nb6hvFtYLHrZsWmacsr*T_VW!+XU>`vJXgSRE0e-R>1+;QbKp2kjJCSOzbjX3Jv9H01X1mXsorR7qc^<0g(%zIHw1ZzuxG-orJSgv}Nml?&rJ6*GwZmKS6A)w&ZYwroM zUaiAWq#)Agc0&D=iH65b#?N17(O5w#5$StIH~ka1k?-dg77*kxYg%m-tkwbroCry( zvVF)&HCkFas_S$PPEJq)0C>6BIfPdkVqwq6aT6g9}8=eRbADyrIfA4NtL zxz_HAV*?_CTRg*~AM8tH6D1YZ_I0r`D(WAMtLJ7fDE)W!o;Qa8%bcE-o{c5EE%N4T z*kQMWIMfjmf^c|9aK8;8e1Ik=?Bl~ilp*N$33APZV(yqwQK->t86AU26C=1#IXThvtV)@+8iOMm`bzHc88}L3I-fj!&sz=-fyyg{V(0P z*69sEB!Q@>``by$83_ME<)ak0!ptm=sA#Ap2K%}mWs^RB0yVpOFON}6`M^M#p9f5x z_p!ch6qxxyh2*tdX>nMnLBwY0A*1?hwK_mKD zl&yjKPL9~2^!hp+48B!CMMIKJjLS*omdu~&|HMJF#dkWo5OWkr(JS#->E z1A@I?`MsLstIHc?a_-ZXK+SIILUO=Iv z8wh{&Ma~1eS>Z-V!DVFBqNmozkPC$3;vB$0=wWMhKZ``(bKn&!A^gmH7S-H5p4wQZ z+$eWLz`>we2Ay1N*f0I*(>Zr;PVO-%9BuLv)$2og!<0>l&ZsCU01X_v+}@@GAZt&# zO2TcE`|hRf>oPQVyAuaGSWKZBt8OKam$GzqT*{A=d#-+v~Emt1cc_&*TkVAt_d zQ;P?GnM~SV=qA04igmj?2|KU!jb!d@1bkWG25|$?Ls!>)f4 zczyQr#Cg62#>7yU_sM_elad-wtq4*GmKuK&vpEq#m;HM8#^R+9LK}R}2R&7}d+$xg zQtFLIX_{7o-SIaQ3z0uyi?AH?^U@GpkXY?U67vTCRU$XwYvnEs`-_FSRnN{r4UPLH z2p6zn_Ah;{D135qM1+L8z@iTwUnB#>l(8uCHLBGOQCRDzIl=Dw-ZsrMB(R+RODZVg~wv4pQK|~~f6qkj3HBbt94{WD@^-*tG6y=op zjxOeiBV`kS{xkTefM|tz5_VJcjv7$8QN1;goXX`RwzIPnP&GL>s1C3B%}eoHUl9P& zilu@dO@2WET`65a&=)YdBSGR!K;lFCdjqh7+21;|K~&<0Xw<61$+j4**z@z8$w<1- z;25c+qx0%RB&!Tr%I$kztjJvLLG1Y{j4L&(Q~Vtaj%TsHbbBn3!&$ zHZtw~}k3eMb|tCaAuHIrGd~NSc+BG*I!&*qz@n10t*lW`;-iNUnd4; zRj@bp5F%KYAP|D+`p+BfqfMl(1@El^N2)Zzr~9s{v$@JMaE=BbtWY9HFb!Zh0|mr& zV%c>A-KB@}p2u%SMHNH3-fRVy93^ASreV6DWJ5CbOwguk-G_k7%JRpL2IoH{Vn|cb zimWv?i!F=iS2;Np7&;g94uR=;R|yRbYX1t36@0^=UH?V*swk-tusK;?>;YnjrDs2Z z{88|bjZ5e3pr73J!IkS6n;O9C7d+CUQpW9i7{{_a zh5>fMTcQBpH7O3LVw|Jn!^OIdaw=j}B08Wv0Xi9k`Y_d@!XQyVW#$T)Wt%%(w=On% zn{2msnyMAKc|F8BklU1qki6Kmu(L0LA_vf2I`z`3?BBI4`1x_LmR?&m@cwN)Mv z#+qAN@TW^)PUQKuUdtT`pMjlo39J~!#UFr74I-7`Xw~L^SFsZs|9n8{oD!7;*iioh zQ3wGD0bpF1C>eVJcAz42C6?CAj6#*;22eHF`n&_|Op9~A8-m2YYMct#7OsMV&ZsDK z;I@W#`Ih|+jby&gYy}4bkV4m^4A6exF{+idx%F$gXsf-O+5$I(C{8Y5g!HoKjj=QqsR>twYq^f34AK)zZ?61Pfa?-yA#mh7Lg)S=eN6 zf_Sb%CzZp!0bKbY*$Z(aC*39$d?0*+>%L)6ZCc9O;dg_k2tTdBsH&-Sz%DZ}HF9`e z*5q?<1p|u>85WlEHrw>f0+K)8hcLo&GLSB)xI8)kL zy7xI&#NQON>sJu8b|saPGB!2|_#!3}s9w}A{Tryl``Ex7~X zK79FkW!F6$&5@C)Wj+lFv%5^G;0a(QDcZTbH$*IjE*q+<_Lmw-8DMz`;bP8fCb8Pv z+t)G3v*BB9fyimPrnac>2yX@%Tcdfwb8ogdL`3%x_>-}#1R9uw3P1H zHn$T*ubYbpI!mUkO;U=@ zb+_)p4+ZatKx;b+wOmN-2yR{t8=LW^<0mN5-n_i+uC6ZI?}J$@DIC_eF9RG&d~9sW zMp*dZe2ewp`IgbAFu;VfQ!%G@=VXTim4X;6J=x6CBmSPG0}(f;H($S5*j}6jIq>4gPqWwU>;Z7(2az0{Es$KE_z5-lz)%m zpQ|FsZ1y5g*i6RR={D0bz&fsOBblu*mBS*Eg}JfrgGOMc0Zhf$9}+*9-E+bvsW2rP zpqQA(2O`E3nKi@FFCJ=5K%hmV#v%FrUL?RyS>2Sx^EsbvvRTec9dVXp#E>he#3$dp zt~~AC%HuL=`qGHT4Yj5Mi4g!UmzohK0Agib;dH)%I8t3&+PoJQC0XeglFQXBihpE> z0Jz89_3PnIfL@FZ$YWt@{fyolah;d@i`@9qc~`v+GA%9UTCG;j!YboVP?L;JgsD<* z&S7NiSXZAt4iNXxpS~B|V}Ml*3(KF0bUyty2aJcM(L|OW5D>Z8m|^@iM&+fGmd?)3 z4#e)*^mzOG$!^=NK}XR1?pgTjuiqQbNOrz2>r%7wo{HjO=^Qht5s!~|aAY9kC;Vve ztMn)pcsHey(VILrHbEZ9e7Y?8W*aQz`bteWO6Q1p45@fiLaL=|3&tn!9;D3CTT+r| zutJ3~V`8FxgwWEB{|(3@UfL}+SS53tvDWNY>UCoB!V6XX*D3=A)e92b-A?36;=9Xl zqqq)fXruyVTI~{wiqRX$NKMXnrvS=FrbYhqS5q(w^Hm|D<$Mu&N z%y8A&sZH&4F(tTXpdcVf^lrv}X+UFV=cs3|2O@+mJhZ>XT)AJ%xV!@l<7c;HJh4FL ziHT=efw75^9>)y{gB^N}V*~%?v?a?>k*A3jK80s;jo(B((x5|X;sn%H`~QoJO%&~8 zT#W&BnwxW|j(i^X3ctL&wuhBhbk>jwuK5J~5n(-1_&n}TiT(Y6Un zm>1v>)6&9Qq-rQ4GM=J8C`{I}3=oV83g=ZDy|S|7ypE@1HpVO4*SFGiJtRlUaI zej|Z;Tst0ZO;ihMvC-udUfm5LC#bH?9;Kq85y=YQi6y`yL1RFNF#`pbSO$LxhQdG^?&cDd(QoGIfoB$-r1e^nR#dCeV$)@ z07{&e5(Uta(MuqISc3L#yRYs;;uQgZ1DtEaNJ=ukGMp?Z^Opns(eOxa;l2K*gTv{< zgZ%t_)!0~2>Ht8RjLcMdmDu~9pOSU}BVsr?NS?kqm^Yf-ZA;R@XfJdCZzEg)la#*|2XaTLbQvp=5xkEIIr$w zt_V2*%(r){%;v`>#Wl!KX(f4LBDx&pwoWJ^bc|{&X1-^3* zMyvzQ1&~1%2J(XtkmM}x7;Q+o^K)#=Sv6i-9DvLSrW@HxCb-OwE8qp zeUJo7WS?`V-9i2(b@!ciJAPnd&O3%(ze%k&9}NwO79xRJ;6~DVx{Gi*W3*vbVO(vM z_pdOS#v9vBWSr_z=_)M;{#=L(!{yQAooNfxjxOUo8GqWRiWaOEn?4}_C}>!jC$Y1| z^m?Dd%Gw&ZCP~|$2+fEGh!Gv|J{nd(K0ex@p@|%5PPwC4W`b}!9_Nf%5$aO1^rh!7 zzX&>g*hVq(TPg1<0_S{M`}zz$J2uc4Z3_Gf#QufNm=`jIAJ54JCL9@+KFn}iXe+NU z>G}+3W_ny4NW(%K0t%O4Z$vnL+eM<*(0IFBVroM`S)--h)1<|PADS(Plwo9KG-`Xh z$)`(2rJ(Q{knRfa0pH>M5E*YuDa}}Nh$ox0Jdx0wK+#LB#Z+F=+qbN#@TH_Y05@P_ z<3LW~rlzJZ>By}2kKzM>Q391$#WzLaZc)vL-=BHstgJMZ=SaGXpt`YP6jwwli9DAX zB+Sx!1=f6$a!(yUdMBNJZEu&Y&>sqtQRjF}B@aPgN02zN+X6C87j6^%OA1Z`jC&%l zM%Hr=wewaDP`vWVliloVgj~2l&HVpqBW!JK{xy(JC8N6ww}^^ak>&NnH}Fjag}cg%Xs5qj*^-LMiOzxmqGz zL!195kIQsN53p=ZIiW7FjbMGoBjj#TWOdC?Vq;jSyqke4H1OoectIL5p5U^5x-$|% z8?e8ReNX`y!Lq&w=_*`6(vIVC&~jl|3>hn}yFOP8WQ8;K+3!hJaisu78y8a{jg!@= z_-+&Er)1U#-6ux_FDT;`sIEl7$;YZ`5$IN4D{leSEugKnIf-zh-*3%;1UQhq$0{NSU8*WBt_xi< zhff|eO#Vpbc*B#C$t>e{-{Wd~jcR%CYT(P^MNsl|wZ+XmXy`#ntoy>D;x8al;JVOI z>H5tNlz3!4mcd5%pePAc!EU$`DDZ3(@!%X#AaA~1uMx(@CxOSsg{v0~spJszSzu#_ z%`*+$%!dX!ovsr~*!G%Fz!F@aIKcb5Z?N|PG=}{_dO5+PTGXe6?6$ybz|E=AYeT@Y zL!S4+Cyd>cAPVKV%4xb!@SLaD{93Jap~2EGLlPX{=%CDJ=jbr@5bnV;5Z10c6yD6t z%(LaKQf2bRSoxJIZm{ZX3D@5@I&Y%!vPESFKP588=;a285J?Ega-4te!gcBO@&fvk z`_xrBrEyQ*U?rFU5DOb&mnkeKEHLW!#B}umswc*8r^8qaJ)d0<#Nq)`RqXa{nSduy z*#bz<197)`BfjV7+kAtj--Dpn65Q{i3j|2~=oOM*G?c+e{ zyu6+mvgW(31W{uJGOfqe!jv}|{-DuqfrnnG)n3=8uPMg7iOwE2+|w6lWp!zklhPeF z!niH3(m*-N< zW(%B4gD>%5=O%>^BV;EfoQylk>m}A@9JLJ9(-r`K@epBJEk?NO$gPfR1_y`#C`;Da z_Tcry!SQSa);ASy3(}OF8BSC0rGifK06+=hz64WH=$S-PYjfnM(!Oe$neUti#r}09 z@&WmZQGXn$0ePQC#-4vJ*^_!{>e;9cH%AhZ5=W&`GSE)7_&TpsuC1*>4(aIVw!by{ z|2bh^Rppf(YIMCIc5GP)I$@Cs#IQZezr2YZ8};~eBCKe9?w7+yYCcZmyanW$v>9x2 zJ}`gt|K*`A*u$gdeM9&$39BtAc%A==06ZeyH|k1RPEH&p7uj=&#Dpyddpn!jp6ZfP zfvuk?XU^`g0Y6tsN(rA%_V@Se3mT{w+``p1)zVrninzYQUwklR_GoMwv^!0-zkV~C@9)Gr?r~gCQaG}Z1dUyV5$}p{nc|!Wy<;5SAV9(RDYV(fB zFS4Lqe4cIs>;PWL_=fzR44Ehy*l#<_c3Al#7l^*Uf4P#*Nf~nS%U9m2*oxoWf_sn` zq%eTN=Jss;pD|y?-Gx?XCp+33tQgE?826Ro%KiDt@1M%lU-5L{7=pYvL|(|ejRH6W zBX223D}w?GhFVyCg>|oRwTCb$NJbB93ZKS6+d#>VC8IUDO-}N~;>V@XAG|g&!=1Ux z+_W?=szV-7c2%v7cZU&wK|F4xT_O6g5Lu##((NA}UfpW;TaXS`YcuNzCG)of2yzE; zN&m(#yc$*J*4%=e)_;~^0M$lA-7%V!FvoGYx4qrw`DJ>ecFuqEvdroH0V;#OrO~=f|ZBuVy6b@>C;}HR6agYt+=2@qANlE3Ll3hSg{-2F>2bzYZlug ziu|$Vnk7H~TMey|VW~_Jv+&9FlL|@gzAwCL zSjhBPAUcRpuk|$z*vD?SD=H#cwUwlB{Z)n))D|FXPSLFM-G-Frn&rb_&RE9hgdVnT>%vqbUTOC6iD0ViM^_ zYZSujELz&OzcjLzWCFL)o)tn80_yLU&qs!bN0}sZUiEc-?%zkFtOec)95aU8Uf0bc zQ8Q51$j*(#o|DsH`VLMUv@PUdNHcbT4s*!GpT>=J&gfh-H7hnWfXzczEOpD&!+81E z#cXYDEX|jDQN}`8{`wXh|=&wCIr06OQzPdEy zX{7cuu=8_p5M^K*<1ytoz4$OGF7=bKJp+42FjlvC?bUg zg&b^EI4A@9Y&&4`i|}V_eVPFGukYaC04k+MP*HYRwU=$bIXI@$fxJ-^CT|3oLynG4 zi!D#B=Z>Ig!_8D&L*17?SpcfCEDBVtl2X|2v12DsCM2lKMo(8F!tHK>GIexrY%nUV zl+r?RkSvwtyq;U_WjCqQ=fWZc;y7#EI>BjrYHDJx-T4NWa7dEh_F+^+#15{B>^9Kz z)$v%^qzwC1$Zl{rca0Wd+2pxg==r*D|3x8ymQ1F-v^V_uxXuQ}ds7oHj-)J2Hspw) zc|_xt5Zd=srb$%|qPEu5jqpb`sFn2b@SrJ-70>ruqYQmE;*JOPD~K!UOm*P4`*sv} zVZ1u}+T}*6ue8mLG*Zhz3Q;S5If%cXpPqrftLJ%m^e-hvWwy}k*h6aze?V|z}8GsQ~YK)JzVT+nc8?IrQ>@bD2i+RFXP z=OjhZTx*xkZ9)V9<95yw5N)6yW^d7MM@mno zb7TGjy!{!xHG*`>7LNd9U9cZ^dq)NEUP^0%6q7$tyvWux(oI9_qX#k{>OZ2nINfP& z^f;oS$v8TGr>r0UeJMjhmJOK#jjc(J^rFn7LV)_$YfZB-))srVdCU46 z!o94pkdB(}$v^JdrUSrV7eIqy}2BxhiP&?oVt3$`Tp$DF}7FWu)0 z3Lw)pum}m)Z<~6bH>UC4Gvl!FVPO%|sVc(s3_C*tbKYv~NPmU_=gkPWjy|UmP=XN_ z658i`nURq#YH8*4aPXB3;1UU$G{|ys*;IaIaB%H^(0x(y{?wN+{#yJElpl?+?zC2n zQF4L2NqQ(bS>oD{A2%3p@KzV{T}~$#+#AWeLoAShIJPD5xP6&L#@Jyqz#-1xAqI>I zRek|7c)N_hrOQPJy2!OhzUN-Z{!E*vwSk5K@Y1Y*V^L3rd!00RWnbR3U4URXzAnzI zD0B|t&D*$KP9(Gro5X|MIK_ z2#LGSEdTo5H|QiB0=#*;txKJ!-AKUuo_vW;nl)WG8Up{}6uBcWIiWZ(CmpChi}MI( zVL{#!U8$qRQ zrO?6N24|(AyID{BjK?eJ0PQ(sP^rj~S~>D8UgOou#>b7{=V#t-WOa37zKL7K#$HX6 zBKJuFmbCX1&U~}?pUT!hU_!o&9}&=hn3C<|;jsx)BSou*IVvFS#Z1FYLj?>jnJbIl z+zRM_qQuP|P(e1vd~|#av>!(-#7sBISy;|z{s3T%--j9si^5OehPF0qU$+MxI~hKa zRf45Z?@U!PhStqA=bIa!##Z2lQBY4YJfojE8lkm1MJ zkq@X^_fMTm=w*cuX%OyLH>d_5?C!Mg?~9fP14w7l++i4#*A`%J{aSn-^J0w{-T>Y+ zteT|q*VbmU6N)AddUbTtK#~~8IhsUDzR92yvHHNP$eji1IbWEGeI?-oj;6V}g3z9% z`}dz;(KtKzOH-2SGo-j^X=_&*ARYq%C!%>an2c!#YO}9p>0}eRwE0sM2kc{c!Dk1h z!1kBMl#!(@kD{P|k6l1)aA-?6_q>*-^m;=((D$dv^+<)x4|+{AOhBuWs;bDK^rZRx z{c!MFKUHpGBH94n&yte1Yi5^1oD;PYuRf0tjF(i!(w0@eZU*Wxl1{^Ps11YI16<>G zd);Hxjmu(pnpQHX(;0FlMJ`)wBmj8 zUNM!aXB)hQh&Y7wKHa)BP&QzgqU6Pu*$3DeP-~fW zHI$=kj8|g-MF$lcX3owPVs?2u-+*3*qcjxfBIg&40{^|$82QF8p;%t?u~)J-<#Atp<|dFWFla)42g!3`$aG zjOp7$$MXI{2p%dl64T==dTp+mn z;ULp*ul-hg4Vs>r30x0sK!^t^vY6>u-@uhNi9=K}G>MK6Dux5ai+R6T?JQ z-pYz(n9LoHyxNfDG9QCna$_1A%l~btY1M70)~h=yp}<)WX9eSkyuy~S$4byp4h~ab2-841NQ%rR5>Lj8?!VcT|+nlDgQPn zP~0ZHWY}hd-e7}KAfddxyo|Iwpthc#6DiBixJ)DS)dBXc7jv_#y#H=O6P5WcYsC<_ zV{wFtV}^-^aPOTKw%SZzr$>{QH^39t5&I5-IA7v#4@sy%9&)Pk-9+(_bqdol#nxz@ zYyO>)Av`wa1mS#F=?>*Y3wA2)93eQE&&t;!>lUzUeX9xD#}%VXEV^75yWycCmwzW* z2>!oD!PTV#BaH;BF>FJCcB{-GXf$jQuPQ8N#gp77f@zrL?@Dx`AB+rwebd?eJ76gC zzXNgh!`-V3Sr}AE%1gp~}iuP}hqLREkeh1GVFNqP-B27$j2FX z+L+aCD)OdAl@;7lKpZOZ;R}4BK`18}A0+wT8NE?ZU7o0DV%-)^Qk3;g?obF~0$$C@tbMEKlz+ z?83ps3BB@fjviVI)4wKFJXkRK zzW|>bsr9CmD4pA28i|DVAOt*5{G$O6(V=5##HwS7BNa0t3jQ;zm+JNKP=^?qpr0x< zwH%g?Vv*^LTm;49)O(^Cc1}8&Pd<0~_i&&60MQL$0b^|o)4rtD)rqdrl2KuygPjis zzYIDu)1bpdF@*X*{g4G=f)avU* z&%v|1%{TtKIBXYuDb>TD&xRnkzbl7ABZU;oGHyZ`_I literal 0 HcmV?d00001 diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/README.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/README.md new file mode 100644 index 0000000..64551ea --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/README.md @@ -0,0 +1,72 @@ +# Geo Content Brief Skill + +## What It Does + +`geo-content-brief-skill` is a reusable skill package for this job: + +> 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。 + +## 中文使用方式 + +1. 输入访谈纪要、关键词、渠道限制、竞品摘要、品牌禁区或已有内容方向。 +2. 先判断请求是否属于“生成内容简报”,而不是完整长文、广告口号或品牌策略。 +3. 按 `references/brief-structure.md` 输出一页中文简报。 +4. 用 `references/review-checklist.md` 检查事实、结构、证据、风险和下一步动作。 +5. 用 `evals/trigger_cases.jsonl` 和 `evals/output_cases.jsonl` 做轻量回归。 + +## How To Use + +1. Load the skill through `SKILL.md`. +2. Start with `reports/intent-dialogue.md` to tighten the real job, outputs, exclusions, and the standards you care about. +3. Open `reports/reference-scan.md` to capture external benchmarks and any user-supplied references worth learning from. +4. Review `reports/intent-confidence.md` to see whether the real job, inputs, outputs, and exclusions are clear enough yet. +5. Open `reports/reference-synthesis.md` to see the GitHub benchmarks plus curated official, research, and principle tracks in one place. +6. Follow the workflow steps in `SKILL.md`. +7. Check `reports/skill-overview.html` for the generated bilingual HTML skill audit report: overview, metrics, capability profile, principle, contract, quality, risk, assets, and iteration roadmap. It defaults to Simplified Chinese and includes an English switch in the top right. +8. Open `reports/review-studio.html` for the one-page Review Studio 2.0 gate view. +9. Record source-line reviewer comments in `reports/review_annotations.md` when review needs follow-up. +10. Open `reports/review-viewer.html` for a compact visual review of the package. +11. Check `reports/output-risk-profile.md` to see likely output mistakes and self-repair checks. +12. Check `reports/artifact-design-profile.md` to see the intended artifact direction, layout patterns, visual quality gates, and anti-patterns. +13. Check `reports/prompt-quality-profile.md` to see the need model, RTF-to-skill mapping, complexity, and prompt-facing quality matrix. +14. Review `reports/skill-ir.json` for the platform-neutral Skill IR contract before platform-specific packaging. +15. Review `reports/compiled_targets.md` to see how Skill IR compiles into OpenAI, Claude, generic, and Agent Skills compatible target contracts. +16. Review `reports/iteration-directions.md` for the three most valuable next moves. +17. Review `reports/system-model.md` to understand the boundary, feedback loops, drift watch, failure map, and highest-leverage next changes. +18. Review `reports/adoption_drift_report.md` to see local-first metadata-only adoption and drift signals. +19. Review `reports/review_waivers.md` to see human reviewer risk approvals and expiry dates. + +## Honest Boundaries + +- This package starts from the current intent frame and should not pretend to cover unclear adjacent jobs. +- The first version should ask for clarification when the real input, output, or exclusion boundary is still fuzzy. +- New structure should be added only when it earns its keep through evidence, validation, or reviewer need. +- It should not fabricate search volume, competitor rankings, customer quotes, or third-party evidence. + +## Package Map + +- `SKILL.md`: trigger and workflow entrypoint +- `agents/interface.yaml`: portable interface metadata +- `manifest.json`: lifecycle and packaging metadata +- `references/brief-structure.md`: 中文 GEO 内容简报结构、字段说明和质量标准 +- `references/review-checklist.md`: 交付前编辑复核清单 +- `evals/trigger_cases.jsonl`: 应触发和不应触发样例 +- `evals/output_cases.jsonl`: 输出结构验收样例 +- `reports/intent-dialogue.md`: front-loaded discovery questions for better boundary design and clearer human alignment +- `reports/intent-confidence.md`: current clarity score, open gaps, and the next follow-up questions worth asking +- `reports/github-benchmark-scan.md`: top public benchmark repositories, extracted patterns, and borrow or avoid notes +- `reports/reference-scan.md`: benchmark notes from public references, user references, and local constraints +- `reports/reference-synthesis.md`: a combined view of GitHub benchmarks plus curated world-class pattern tracks +- `reports/output-risk-profile.md`: predicted output failure modes and self-repair constraints for this skill +- `reports/artifact-design-profile.md`: artifact-specific design direction, layout patterns, visual quality gates, and anti-patterns +- `reports/prompt-quality-profile.md`: prompt-facing need model, RTF mapping, complexity, and quality matrix +- `reports/system-model.md`: systems-thinking model for boundary, feedback loops, drift, failure patterns, and leverage points +- `reports/skill-ir.json`: platform-neutral 2.0 Skill IR contract for trigger, workflow, resources, evals, risk, and governance +- `reports/compiled_targets.md`: target compiler report showing generated contracts, adapter modes, preserved semantics, warnings, and unsupported features +- `reports/skill-overview.html`: white-background bilingual HTML skill audit report with sticky four-character Chinese navigation, a top-right language switch, metrics, SVG charts, contract boundary, quality review, risk governance, assets, and iteration roadmap +- `reports/review-studio.html`: Review Studio 2.0 gate page for intent, trigger, output eval, context, runtime conformance, trust, atlas, and release readiness +- `reports/review-viewer.html`: compact review page for architecture, usage, feedback, and next steps +- `reports/iteration-directions.md`: the top three next iteration directions +- `reports/adoption_drift_report.md`: local-first metadata-only telemetry summary for adoption, missed triggers, bad outputs, script errors, and review drift +- `reports/review_waivers.md`: human reviewer risk approval ledger for warning acceptance and expiry +- `reports/review_annotations.md`: source-line reviewer comments linked to Review Studio gates diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/SKILL.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/SKILL.md new file mode 100644 index 0000000..475ff93 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/SKILL.md @@ -0,0 +1,57 @@ +--- +name: geo-content-brief-skill +description: "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" +--- + +# Geo Content Brief Skill + +## When To Use This Skill + +- 用户提供访谈纪要、关键词、竞品片段、产品卖点或渠道限制,希望沉淀为中文内容简报。 +- 团队需要把零散输入转换成可执行的 GEO 内容选题、角度、结构、证据和交付要求。 +- 需要先明确触发边界、输入缺口和输出验收标准,再进入内容生产。 + +## Workflow + +1. 读取用户输入,识别目标受众、内容渠道、关键词、品牌边界、禁止表达和必须覆盖的信息。 +2. 对照 `references/brief-structure.md` 生成简报骨架:目标、洞察、角度、关键词、结构、证据、风险和验收标准。 +3. 如果缺少核心输入,先提出最多三个澄清问题,不用假设补全用户没有提供的事实。 +4. 用 `evals/trigger_cases.jsonl` 检查当前请求是否属于 GEO 内容简报场景。 +5. 用 `evals/output_cases.jsonl` 检查输出是否包含输入摘要、内容结构、证据要求、禁区和下一步动作。 +6. 交付前回看渠道限制和品牌禁区,删除空泛营销话术、无来源断言和不可执行建议。 + +## Inputs + +- 访谈纪要、销售反馈、客户问题或专家观点。 +- 关键词、搜索意图、竞品页面摘要或排名线索。 +- 目标渠道、目标读者、品牌口径、禁用词和交付格式。 + +## Outputs + +- 一页中文内容简报,包含目标、受众、核心洞察、推荐角度、文章结构、关键词布置、证据需求和风险提示。 +- 缺口清单,说明继续生产前必须补齐的信息。 +- 下一步动作,面向写作者、编辑或 SEO/GEO 负责人。 + +## Output Quality Guardrails + +- Before final output, apply the likely failure modes in `reports/output-risk-profile.md` when that report is present. +- Before rendering reports, tutorials, review pages, dashboards, or visual artifacts, apply the artifact direction and visual quality gates in `reports/artifact-design-profile.md` when that report is present. +- When prompt behavior, role design, dialogue quality, or output contracts matter, apply `reports/prompt-quality-profile.md` when that report is present. +- Before adding more structure, apply the boundary, feedback-loop, drift, and leverage-point checks in `reports/system-model.md` when that report is present. +- Repair generic headings, cluttered notes, fragile visual assumptions, weak tables, and missing verification cues before handing work back. +- Map role, task, and format into skill behavior rather than copying a large prompt template into `SKILL.md`. +- Let the artifact's content choose the visual system; do not copy a fixed palette or report style from another skill without a clear reason. +- If output-specific evidence is missing, state the gap instead of inventing screenshots, citations, data, or examples. + +## Honest Boundaries + +- 不直接代写完整长文,不替代品牌策略判断,不伪造竞品数据、搜索量、排名或引用来源。 +- 当用户只想要标题灵感、广告文案、舆情分析或完整文章撰写时,不应触发本 Skill。 +- 缺少目标读者、渠道或关键词时,先收紧输入,再输出简报。 + +## Reference Map + +- `references/brief-structure.md`: 中文 GEO 内容简报结构、字段说明和质量标准。 +- `references/review-checklist.md`: 交付前的编辑复核清单。 +- `evals/trigger_cases.jsonl`: 应触发和不应触发样例。 +- `evals/output_cases.jsonl`: 输出结构验收样例。 diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/agents/interface.yaml b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/agents/interface.yaml new file mode 100644 index 0000000..e74f5ef --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/agents/interface.yaml @@ -0,0 +1,29 @@ +interface: + display_name: "Geo Content Brief Skill" + short_description: "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。" + default_prompt: "当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。" +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "openai" + - "claude" + - "generic" + - "vscode" + activation: + mode: "manual" + paths: + - "SKILL.md" + - "references/brief-structure.md" + - "evals/trigger_cases.jsonl" + execution: + context: "inline" + shell: "bash" + trust: + source_tier: "local" + remote_inline_execution: "forbid" + remote_metadata_policy: "allow-metadata-only" + degradation: + openai: "metadata-adapter" + claude: "neutral-source-plus-adapter" + generic: "neutral-source" + vscode: "agent-skills-source-with-vscode-notes" diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/output_cases.jsonl b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/output_cases.jsonl new file mode 100644 index 0000000..d0b9c73 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/output_cases.jsonl @@ -0,0 +1,2 @@ +{"id":"brief_minimum_contract","input":"访谈纪要、关键词、渠道、品牌禁区","must_include":["输入摘要","目标读者","搜索意图","内容角度","结构建议","证据需求","风险禁区","下一步动作"],"must_not_include":["虚构搜索量","无来源竞品排名","完整长文正文"]} +{"id":"missing_evidence_behavior","input":"只有关键词,没有访谈、渠道和品牌限制","must_include":["缺口清单","最多三个澄清问题","待补充证据"],"must_not_include":["假设用户品牌口径","编造客户原话"]} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/trigger_cases.jsonl b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/trigger_cases.jsonl new file mode 100644 index 0000000..4358b45 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/evals/trigger_cases.jsonl @@ -0,0 +1,4 @@ +{"id":"should_geo_interview_notes","input":"这里有一段客户访谈和 8 个 GEO 关键词,帮我整理成一份中文内容简报,给写作者用。","expected":"trigger","reason":"用户明确要把访谈和关键词整理为内容简报。"} +{"id":"should_competitor_brief","input":"把这些竞品页面摘要、品牌禁区和知乎渠道限制整理成一页选题简报。","expected":"trigger","reason":"输入包含竞品、品牌边界和渠道约束,输出是一页简报。"} +{"id":"should_not_full_article","input":"根据这些关键词直接帮我写一篇 3000 字完整文章。","expected":"no_trigger","reason":"用户要完整长文写作,不是生成内容简报。"} +{"id":"should_not_ad_copy","input":"给这个活动想 20 条广告口号,越短越好。","expected":"no_trigger","reason":"广告口号生成不属于 GEO 内容简报。"} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/manifest.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/manifest.json new file mode 100644 index 0000000..ca9462d --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "geo-content-brief-skill", + "version": "0.1.0", + "owner": "Yao Team", + "updated_at": "2026-06-15", + "status": "active", + "maturity_tier": "production", + "lifecycle_stage": "production-preview", + "context_budget_tier": "production", + "review_cadence": "per-release", + "skill_archetype": "production", + "target_platforms": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "factory_components": [ + "references", + "scripts", + "reports" + ] +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/brief-structure.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/brief-structure.md new file mode 100644 index 0000000..e6cfd13 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/brief-structure.md @@ -0,0 +1,23 @@ +# GEO 内容简报结构 + +## 目标 + +把零散材料整理成写作者可以直接执行的一页内容简报。简报应帮助团队快速判断:写给谁、解决什么问题、采用什么角度、需要哪些证据、哪些话不能说。 + +## 推荐字段 + +- 项目背景:产品、业务目标、目标渠道和期望交付物。 +- 目标读者:读者身份、已知问题、决策阶段和内容理解门槛。 +- 搜索意图:核心关键词、近义词、长尾问题和需要避开的无关意图。 +- 内容角度:建议主张、差异化角度、可引用观点和与竞品的区分点。 +- 结构建议:标题方向、章节顺序、每节回答的问题和必要证据。 +- 证据需求:内部数据、案例、截图、第三方来源、专家观点或客户原话。 +- 风险禁区:禁止承诺、敏感表述、未证实数据、品牌不采用的语气。 +- 验收标准:完成后需要检查的结构、事实、语气、关键词和行动建议。 + +## 质量标准 + +- 每个推荐角度都应能追溯到输入材料或明确标记为待验证假设。 +- 缺少事实证据时写“待补充证据”,不要编造来源。 +- 输出要服务内容生产交接,避免泛泛的市场建议。 +- 中文表达要具体、可执行、便于编辑继续拆稿。 diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/review-checklist.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/review-checklist.md new file mode 100644 index 0000000..43d7bdb --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/references/review-checklist.md @@ -0,0 +1,11 @@ +# 内容简报复核清单 + +交付前按以下顺序检查: + +1. 输入摘要是否覆盖访谈、关键词、渠道限制和品牌禁区。 +2. 目标读者是否具体到角色、问题和决策阶段。 +3. 推荐角度是否能从输入材料中找到依据。 +4. 文章结构是否能直接分配给写作者执行。 +5. 证据需求是否列出已拥有证据和待补证据。 +6. 风险提示是否包含禁用表达、未证实数据和不应承诺的结果。 +7. 下一步动作是否明确谁继续补材料、谁写稿、谁审核。 diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.json new file mode 100644 index 0000000..f1d576e --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.json @@ -0,0 +1,62 @@ +{ + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-15T09:03:27Z", + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "summary": { + "event_count": 0, + "adoption_sample_count": 0, + "activation_count": 0, + "accepted_count": 0, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "no-data", + "event_types": {}, + "failure_types": {}, + "source_types": {}, + "command_counts": {} + }, + "adoption_by_skill": [], + "next_iteration_candidates": [ + { + "signal": "no telemetry", + "recommendation": "Start with a small metadata-only sample before using telemetry for release decisions." + } + ], + "recent_events": [], + "failures": [], + "artifacts": { + "events_jsonl": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/telemetry_events.jsonl", + "json": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.json", + "markdown": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.md" + } +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.md new file mode 100644 index 0000000..f4e68f5 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.md @@ -0,0 +1,36 @@ +# Adoption And Drift Report + +Local-first, metadata-only telemetry for skill operations. Raw prompts, outputs, transcripts, and notes are not allowed in the event stream. + +## Summary + +- Events: `0` +- Adoption samples: `0` +- Activation events: `0` +- Adoption rate: `0` +- Missed trigger signals: `0` +- Bad output signals: `0` +- Script error signals: `0` +- Review overdue signals: `0` +- Risk band: `no-data` + +## Privacy Contract + +- Storage is local-first. +- Events are metadata-only. +- Raw user prompts, model outputs, transcripts, notes, and messages are blocked. +- Distributed packages should include this aggregate report, not raw `reports/telemetry_events.jsonl`. + +## Adoption By Skill + +| Skill | Events | Adoption Samples | Accepted | Edited | Rejected | Missed | Adoption Rate | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| `none` | 0 | 0 | 0 | 0 | 0 | 0 | 0 | + +## Next Iteration Candidates + +- `no telemetry`: Start with a small metadata-only sample before using telemetry for release decisions. + +## Recent Metadata Events + +- No metadata events captured yet. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.json new file mode 100644 index 0000000..e988548 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.json @@ -0,0 +1,115 @@ +{ + "skill_name": "geo-content-brief-skill", + "design_system": "metric editorial", + "primary_artifact": { + "key": "report", + "label": "Report or brief", + "direction": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "matched_keywords": [ + "report", + "brief", + "简报" + ] + }, + "artifact_families": [ + { + "key": "report", + "label": "Report or brief", + "score": 3, + "matched_keywords": [ + "report", + "brief", + "简报" + ], + "direction": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail." + }, + { + "key": "review_viewer", + "label": "Review viewer", + "score": 2, + "matched_keywords": [ + "review", + "审查" + ], + "direction": "Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix." + }, + { + "key": "dashboard", + "label": "Dashboard or metrics page", + "score": 2, + "matched_keywords": [ + "dashboard", + "table" + ], + "direction": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation." + }, + { + "key": "tutorial", + "label": "Tutorial or guide", + "score": 1, + "matched_keywords": [ + "tutorial" + ], + "direction": "Progressive instructional layout with domain-specific section names, short success checks, and examples close to the user's real input." + }, + { + "key": "visual_capture", + "label": "Screenshot or visual evidence", + "score": 1, + "matched_keywords": [ + "screenshot" + ], + "direction": "Evidence-led visual artifact that records source, viewport, crop intent, and the exact region the reader should inspect." + } + ], + "layout_patterns": [ + "thesis", + "evidence blocks", + "decision table", + "risks", + "next actions", + "summary", + "variant comparison", + "evidence" + ], + "design_tokens": { + "type": [ + "Use a distinctive display face or serif for major claims when the artifact is editorial.", + "Use a restrained sans for dense body text and technical details.", + "Use mono only for metadata, paths, commands, labels, and evidence tags." + ], + "color": [ + "Choose colors from the artifact's domain, brand, or evidence mood.", + "Do not default to Kami parchment, purple gradients, or generic SaaS blue unless the content justifies it.", + "Keep accent color limited to decisions, active states, risk, or section anchors." + ], + "spacing": [ + "Prefer clear grid rhythm over floating decorative cards.", + "Increase whitespace around decisions and shrink whitespace around supporting metadata.", + "Split dense content instead of shrinking type or adding scroll traps." + ], + "components": [ + "Use cards for grouped evidence, tables for comparisons, callouts for decisions, and timelines for sequence.", + "Avoid cards inside cards.", + "Keep reviewer-only detail visible but visually quieter than user-facing guidance." + ] + }, + "quality_gates": [ + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers.", + "Make differences visible instead of hiding them in prose.", + "Separate author-facing recommendations from reviewer-only evidence.", + "Surface conflicts clearly and keep routine benchmark synthesis quiet.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each." + ], + "anti_patterns": [ + "Do not copy Kami's fixed parchment background as a default.", + "Do not use generic purple gradients, glass cards, or stock SaaS hero sections unless the content calls for them.", + "Do not let Markdown tables become the default shape for every comparison or explanation.", + "Do not turn reviewer evidence into user-facing clutter.", + "Do not invent screenshots, citations, charts, or UI states." + ], + "reviewer_note": "Use this profile to judge whether the generated artifacts feel designed for their job, not merely rendered." +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.md new file mode 100644 index 0000000..6e09e08 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/artifact-design-profile.md @@ -0,0 +1,93 @@ +# Artifact Design Profile + +Skill: `geo-content-brief-skill` +Design system: `metric editorial` + +## Primary Artifact Direction + +**Report or brief** + +High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail. + +## Matched Artifact Families + +### Report or brief +- Matched keywords: report, brief, 简报 +- Score: `3` +- Direction: High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail. + +### Review viewer +- Matched keywords: review, 审查 +- Score: `2` +- Direction: Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix. + +### Dashboard or metrics page +- Matched keywords: dashboard, table +- Score: `2` +- Direction: Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation. + +### Tutorial or guide +- Matched keywords: tutorial +- Score: `1` +- Direction: Progressive instructional layout with domain-specific section names, short success checks, and examples close to the user's real input. + +### Screenshot or visual evidence +- Matched keywords: screenshot +- Score: `1` +- Direction: Evidence-led visual artifact that records source, viewport, crop intent, and the exact region the reader should inspect. + +## Layout Patterns To Prefer + +- thesis +- evidence blocks +- decision table +- risks +- next actions +- summary +- variant comparison +- evidence + +## Design Tokens + +### Type +- Use a distinctive display face or serif for major claims when the artifact is editorial. +- Use a restrained sans for dense body text and technical details. +- Use mono only for metadata, paths, commands, labels, and evidence tags. + +### Color +- Choose colors from the artifact's domain, brand, or evidence mood. +- Do not default to Kami parchment, purple gradients, or generic SaaS blue unless the content justifies it. +- Keep accent color limited to decisions, active states, risk, or section anchors. + +### Spacing +- Prefer clear grid rhythm over floating decorative cards. +- Increase whitespace around decisions and shrink whitespace around supporting metadata. +- Split dense content instead of shrinking type or adding scroll traps. + +### Components +- Use cards for grouped evidence, tables for comparisons, callouts for decisions, and timelines for sequence. +- Avoid cards inside cards. +- Keep reviewer-only detail visible but visually quieter than user-facing guidance. + +## Quality Gates + +- Keep the first screen useful without requiring the reader to parse every detail. +- Use tables only for comparisons; move explanations below the table. +- Keep source notes readable without flooding the body with markers. +- Make differences visible instead of hiding them in prose. +- Separate author-facing recommendations from reviewer-only evidence. +- Surface conflicts clearly and keep routine benchmark synthesis quiet. +- Avoid paragraph-heavy table cells. +- Keep charts tied to one analytical question each. + +## Anti-Patterns + +- Do not copy Kami's fixed parchment background as a default. +- Do not use generic purple gradients, glass cards, or stock SaaS hero sections unless the content calls for them. +- Do not let Markdown tables become the default shape for every comparison or explanation. +- Do not turn reviewer evidence into user-facing clutter. +- Do not invent screenshots, citations, charts, or UI states. + +## Reviewer Note + +Use this profile to judge whether the generated artifacts feel designed for their job, not merely rendered. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.json new file mode 100644 index 0000000..64a319d --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.json @@ -0,0 +1,2229 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-15", + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "schema_version": "1.0", + "target": "openai", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "openai", + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "degradation_strategy": "metadata-adapter", + "target_runtime": { + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "target_transform": { + "target": "openai", + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "metadata-adapter", + "permission_representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation_policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "resource_strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "script_strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "permission_enforcement": "metadata-only" + }, + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "claude", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "claude", + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "degradation_strategy": "neutral-source-plus-adapter", + "target_runtime": { + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Claude-compatible neutral source folder with adapter notes" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "target_transform": { + "target": "claude", + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source-plus-adapter", + "permission_representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation_policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "resource_strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "script_strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "permission_enforcement": "metadata-fallback" + }, + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "generic", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "generic", + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills compatible neutral package" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "target_transform": { + "target": "generic", + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "targets/generic/adapter.json::target_permission_contract", + "native_surface": "Agent Skills compatible neutral package", + "activation_policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "resource_strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "script_strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "agent-skills-compatible", + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills standard source tree" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "target_transform": { + "target": "agent-skills-compatible", + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "adapter metadata", + "native_surface": "Agent Skills standard source tree", + "activation_policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "resource_strategy": "Keep optional directories as relative resources next to SKILL.md.", + "script_strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "vscode", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "vscode", + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "target_runtime": { + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "VS Code/Copilot Agent Skills project or user scope" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "target_transform": { + "target": "vscode", + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "permission_representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation_policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "resource_strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "script_strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "permission_enforcement": "client-or-workspace-trust" + }, + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [], + "failures": [] + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "json": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.json", + "markdown": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.md" + } +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.md new file mode 100644 index 0000000..fbcb81f --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.md @@ -0,0 +1,73 @@ +# Compiled Targets + +- OK: `True` +- Targets: `5` +- Pass: `5` +- Warn: `0` +- Block: `0` + +## Target Transforms + +| Target | Status | Native Surface | Adapter Mode | Permissions | Degradation | Generated Files | +| --- | --- | --- | --- | --- | --- | --- | +| `openai` | `pass` | OpenAI-style interface metadata plus neutral Agent Skills source | `metadata-adapter` | `none` | `metadata-adapter` | targets/openai/adapter.json, targets/openai/agents/openai.yaml | +| `claude` | `pass` | Claude-compatible neutral source folder with adapter notes | `neutral-source-plus-adapter` | `none` | `neutral-source-plus-adapter` | targets/claude/adapter.json, targets/claude/README.md | +| `generic` | `pass` | Agent Skills compatible neutral package | `agent-skills-compatible` | `none` | `neutral-source` | targets/generic/adapter.json | +| `agent-skills-compatible` | `pass` | Agent Skills standard source tree | `neutral-agent-skills-source` | `none` | `neutral-source` | SKILL.md, agents/interface.yaml | +| `vscode` | `pass` | VS Code/Copilot Agent Skills project or user scope | `vscode-agent-skills-adapter` | `none` | `agent-skills-source-with-vscode-notes` | targets/vscode/adapter.json, targets/vscode/README.md | + +## Native Behavior Contracts + +### openai + +- Native surface: OpenAI-style interface metadata plus neutral Agent Skills source +- Activation: Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata. +- Resources: Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file. +- Scripts: Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution. +- Permission enforcement: `metadata-only`; native enforcement `False` +- Review artifacts: targets/openai/agents/openai.yaml, targets/openai/adapter.json, reports/review-studio.html + +### claude + +- Native surface: Claude-compatible neutral source folder with adapter notes +- Activation: Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata. +- Resources: Preserve the source tree directly; write target notes in targets/claude/README.md. +- Scripts: Scripts remain local package resources and must be reviewed through trust and permission reports before use. +- Permission enforcement: `metadata-fallback`; native enforcement `False` +- Review artifacts: targets/claude/README.md, targets/claude/adapter.json, reports/review-studio.html + +### generic + +- Native surface: Agent Skills compatible neutral package +- Activation: Use SKILL.md name and description; consumers decide automatic or manual activation. +- Resources: Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources. +- Scripts: Expose script and permission metadata for downstream clients or installers to enforce. +- Permission enforcement: `consumer-enforced-or-metadata-only`; native enforcement `False` +- Review artifacts: targets/generic/adapter.json, reports/review-studio.html + +### agent-skills-compatible + +- Native surface: Agent Skills standard source tree +- Activation: Use SKILL.md frontmatter name and description for progressive disclosure. +- Resources: Keep optional directories as relative resources next to SKILL.md. +- Scripts: Scripts remain local optional resources and should advertise --help when executable. +- Permission enforcement: `consumer-enforced-or-metadata-only`; native enforcement `False` +- Review artifacts: SKILL.md, agents/interface.yaml, reports/review-studio.html + +### vscode + +- Native surface: VS Code/Copilot Agent Skills project or user scope +- Activation: Use folder name plus SKILL.md name/description; keep description under platform limits. +- Resources: Install as project or user scoped skill source, preserving relative references and scripts. +- Scripts: Scripts require workspace trust and operator/client approval outside this compiler. +- Permission enforcement: `client-or-workspace-trust`; native enforcement `False` +- Review artifacts: SKILL.md, agents/interface.yaml, reports/review-studio.html + + +## Failures + +- None + +## Warnings + +- None diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.json new file mode 100644 index 0000000..973a08e --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.json @@ -0,0 +1,83 @@ +{ + "score": 10, + "band": "low", + "gate_passed": false, + "strengths": [], + "gaps": [ + { + "key": "job_specificity", + "label": "Recurring job is still generic", + "reason": "The current job statement sounds more like a packaging goal than a concrete repeated task.", + "severity": "high" + }, + { + "key": "real_inputs", + "label": "Real inputs are missing", + "reason": "Without real inputs, it is hard to choose assets, scripts, or examples.", + "severity": "high" + }, + { + "key": "primary_output", + "label": "Primary output is missing", + "reason": "The package does not yet know what it must hand back.", + "severity": "high" + }, + { + "key": "exclusions", + "label": "Near-neighbor exclusions are missing", + "reason": "The route may blur into nearby requests without an exclusion list.", + "severity": "high" + }, + { + "key": "constraints", + "label": "Constraints are missing", + "reason": "The package does not yet know which tradeoffs matter most.", + "severity": "high" + }, + { + "key": "standards", + "label": "Quality bar is implied, not explicit", + "reason": "The first evaluation target is still underspecified.", + "severity": "medium" + } + ], + "follow_up_questions": [ + { + "slot": "job", + "question": "If you say it plainly, what concrete repeated task should this skill own every time?", + "why": "A skill needs a real recurring job, not only a generic packaging goal.", + "list": false, + "label": "Recurring job is still generic", + "severity": "high" + }, + { + "slot": "real_inputs", + "question": "What material will people actually hand to this skill in practice?", + "why": "Real input shape decides whether references, scripts, or examples are needed.", + "list": true, + "label": "Real inputs are missing", + "severity": "high" + }, + { + "slot": "primary_output", + "question": "What finished hand-back should this skill return so the next person can keep moving?", + "why": "The output is the anchor for package design and review.", + "list": false, + "label": "Primary output is missing", + "severity": "high" + } + ], + "anchor_sentence": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "recommended_action": "Pause before deep authoring and close the highest-leverage gaps first.", + "context": { + "job": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "real_inputs": [], + "primary_output": "", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [] + } +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.md new file mode 100644 index 0000000..f06d664 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-confidence.md @@ -0,0 +1,32 @@ +# Intent Confidence + +- Confidence score: `10/100` +- Confidence band: `low` +- Gate passed: `False` +- Recommended action: Pause before deep authoring and close the highest-leverage gaps first. + +## Current Reading + +将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。 + +## Strong Signals + +- No strong signals yet. + +## Gaps To Close + +- **Recurring job is still generic** (`high`): The current job statement sounds more like a packaging goal than a concrete repeated task. +- **Real inputs are missing** (`high`): Without real inputs, it is hard to choose assets, scripts, or examples. +- **Primary output is missing** (`high`): The package does not yet know what it must hand back. +- **Near-neighbor exclusions are missing** (`high`): The route may blur into nearby requests without an exclusion list. +- **Constraints are missing** (`high`): The package does not yet know which tradeoffs matter most. +- **Quality bar is implied, not explicit** (`medium`): The first evaluation target is still underspecified. + +## Follow-Up Questions + +- **If you say it plainly, what concrete repeated task should this skill own every time?** + - Why: A skill needs a real recurring job, not only a generic packaging goal. +- **What material will people actually hand to this skill in practice?** + - Why: Real input shape decides whether references, scripts, or examples are needed. +- **What finished hand-back should this skill return so the next person can keep moving?** + - Why: The output is the anchor for package design and review. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-context.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-context.json new file mode 100644 index 0000000..6a5c444 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-context.json @@ -0,0 +1,11 @@ +{ + "job": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "real_inputs": [], + "primary_output": "", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [] +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.json new file mode 100644 index 0000000..ab53569 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.json @@ -0,0 +1,79 @@ +{ + "skill_name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "focus": "trigger-and-output", + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", + "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster." + ], + "opening_styles": [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。" + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。" + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。" + } + ], + "optional_scaffold": [ + "The repeated job it should reliably handle", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse" + ], + "questions": [ + { + "question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?", + "why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape." + }, + { + "question": "When someone reaches for this skill in the real world, what materials will they actually hand to it?", + "why": "Input shape decides whether references, scripts, or templates are needed." + }, + { + "question": "What finished output should it hand back so the user can immediately keep moving?", + "why": "Outputs should drive the package structure before extra guidance is added." + }, + { + "question": "Which nearby requests should this skill politely refuse so the boundary stays clean?", + "why": "The exclusion list is the fastest route to better trigger quality." + }, + { + "question": "What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?", + "why": "Constraints decide how much structure, packaging, and review the skill actually needs." + }, + { + "question": "Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example?", + "why": "A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material." + }, + { + "question": "What repeated manual step should become a deterministic asset first?", + "why": "This usually reveals whether a script or reference should be created next." + } + ], + "output": { + "capability_sentence": "Geo Content Brief Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", + "required_capture": [ + "recurring job", + "real inputs", + "required outputs", + "exclusions", + "constraints", + "reference preferences", + "first evaluation target" + ], + "recommended_first_gate": "trigger and boundary" + } +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.md new file mode 100644 index 0000000..98d3efd --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/intent-dialogue.md @@ -0,0 +1,75 @@ +# Intent Dialogue + +Skill: `geo-content-brief-skill` + +## Opening Frame + +Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that. + +## Opening Tone Options + +### 温柔陪伴型 + +- Best when: 用户想法还散、还在试探,或者需要先被接住。 +- Example: 我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。 + +### 专业教练型 + +- Best when: 用户目标比较明确,希望被高效带着走。 +- Example: 我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。 + +### 共创伙伴型 + +- Best when: 用户已经有一些想法,希望一起打磨,不想被填表。 +- Example: 我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。 + +## Conversation Path + +1. Start with the user's own words, not package vocabulary. +2. Reflect the job, output, and non-goals back in one clean sentence. +3. Only then offer a tiny scaffold if it would help the user move faster. + +## Why Start Here + +Use this short dialogue before deep authoring. The goal is to learn the real job, output, exclusions, and constraints so the first package is small but accurate. + +## Current Anchor + +- Title: `Geo Content Brief Skill` +- Description: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。 +- Focus: `trigger-and-output` +- Reference note: If you already have examples you admire, bring them in. We will learn the pattern, not copy the source. + +## Questions To Ask + +1. If this skill worked beautifully, what recurring job would it reliably handle for the user every time? + Why: This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape. +2. When someone reaches for this skill in the real world, what materials will they actually hand to it? + Why: Input shape decides whether references, scripts, or templates are needed. +3. What finished output should it hand back so the user can immediately keep moving? + Why: Outputs should drive the package structure before extra guidance is added. +4. Which nearby requests should this skill politely refuse so the boundary stays clean? + Why: The exclusion list is the fastest route to better trigger quality. +5. What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit? + Why: Constraints decide how much structure, packaging, and review the skill actually needs. +6. Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example? + Why: A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material. +7. What repeated manual step should become a deterministic asset first? + Why: This usually reveals whether a script or reference should be created next. + +## Capture Before Drafting + +- Capability sentence: Geo Content Brief Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily. +- Recommended first gate: `trigger and boundary` +- Tiny optional scaffold: + - The repeated job it should reliably handle + - The real inputs people will hand to it + - The useful output it should hand back + - What it should clearly refuse +- Capture: `recurring job` +- Capture: `real inputs` +- Capture: `required outputs` +- Capture: `exclusions` +- Capture: `constraints` +- Capture: `reference preferences` +- Capture: `first evaluation target` diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.json new file mode 100644 index 0000000..59ef33e --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.json @@ -0,0 +1,52 @@ +{ + "summary": { + "skill_name": "geo-content-brief-skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "maturity_tier": "scaffold", + "selection_rule": "Pick the three smallest next steps that increase reliability more than they increase context cost.", + "recommended_now": "Tighten trigger and exclusions", + "recommended_now_why": "The package needs clearer near-neighbor exclusions before it grows.", + "defer_for_now": "Promote from scaffold to production-ready" + }, + "directions": [ + { + "priority": 1, + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations.", + "do_now": "Do this first.", + "wait_on": "Wait to add broader structure until this move clearly improves reliability." + }, + { + "priority": 2, + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + }, + { + "priority": 3, + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + } + ] +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.md new file mode 100644 index 0000000..62453b1 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/iteration-directions.md @@ -0,0 +1,62 @@ +# Iteration Directions + +Skill: `geo-content-brief-skill` + +- Maturity tier: `scaffold` +- Selection rule: Pick the three smallest next steps that increase reliability more than they increase context cost. +- Start here: `Tighten trigger and exclusions` +- Why first: The package needs clearer near-neighbor exclusions before it grows. +- Defer for now: `Promote from scaffold to production-ready` + +## Top 3 Next Moves + +### 1. Tighten trigger and exclusions + +- Why now: The package needs clearer near-neighbor exclusions before it grows. +- Timing: Do this first. +- Recommended actions: + - Add 3 to 5 should-trigger and should-not-trigger examples. +- Unlocks: Cleaner routing and fewer accidental activations. +- Wait on: Wait to add broader structure until this move clearly improves reliability. + + - Refine the frontmatter description to name the recurring job and non-goals. +- Unlocks: Cleaner routing and fewer accidental activations. +- Wait on: Wait to add broader structure until this move clearly improves reliability. + + - Run a first trigger evaluation pass before expanding the package. +- Unlocks: Cleaner routing and fewer accidental activations. +- Wait on: Wait to add broader structure until this move clearly improves reliability. + +### 2. Add the first execution asset + +- Why now: The package is still mostly prose. Add one asset that removes repeated manual work. +- Timing: Do this after the first move lands cleanly. +- Recommended actions: + - Move stable procedural guidance into references if users will need it repeatedly. +- Unlocks: Stronger execution quality without bloating the entrypoint. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Create one deterministic helper script if a repeated step can be executed instead of described. +- Unlocks: Stronger execution quality without bloating the entrypoint. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Keep the main SKILL.md compact and route-oriented. +- Unlocks: Stronger execution quality without bloating the entrypoint. +- Wait on: Wait until the package has evidence that this extra structure is justified. + +### 3. Promote from scaffold to production-ready + +- Why now: The first version exists; the next gain usually comes from adding the smallest useful gates. +- Timing: Do this after the first move lands cleanly. +- Recommended actions: + - Decide whether this skill is personal, team-reused, or library-grade. +- Unlocks: A clearer path from exploratory package to maintained asset. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Add only the gates that match that risk level. +- Unlocks: A clearer path from exploratory package to maintained asset. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Record lifecycle metadata and review cadence once reuse becomes real. +- Unlocks: A clearer path from exploratory package to maintained asset. +- Wait on: Wait until the package has evidence that this extra structure is justified. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.json new file mode 100644 index 0000000..35b20fb --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.json @@ -0,0 +1,147 @@ +{ + "skill_name": "geo-content-brief-skill", + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "table", + "report", + "brief" + ], + "score": 4, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "screenshot", + "visual", + "screen" + ], + "score": 3, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "citation", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "copy", + "content" + ], + "score": 2, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + }, + { + "key": "tutorial_quality", + "label": "Tutorial quality", + "matched_keywords": [ + "tutorial" + ], + "score": 1, + "risks": [ + "Generic section headings make the tutorial feel templated instead of fitted to the learner's task.", + "Steps may explain what to do without naming the exact check that proves the step worked.", + "Examples can become too abstract when the input material is concrete." + ], + "constraints": [ + "Write headings from the user's domain nouns and desired outcome, not from generic labels like Overview or Key Points.", + "Pair each major step with a visible success check or expected intermediate output.", + "Use one concrete worked example before adding broad principles." + ], + "self_repair": [ + "Replace generic H2/H3 headings with task-specific headings before final output.", + "Scan every numbered step for a missing verification cue." + ] + } + ], + "top_risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims." + ], + "output_constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability." + ], + "self_repair_checks": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards.", + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state.", + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ], + "reviewer_note": "Use this report before deepening the package and again before approving example outputs." +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.md new file mode 100644 index 0000000..547566b --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/output-risk-profile.md @@ -0,0 +1,60 @@ +# Output Risk Profile + +Skill: `geo-content-brief-skill` + +## Why This Exists + +Generated skills often fail in small output details: generic headings, cluttered citations, fragile screenshots, weak Markdown rendering, or missing execution assumptions. This profile predicts the most likely output mistakes before the skill is used heavily. + +## Matched Risk Families + +### Markdown readability +- Matched keywords: md, table, report, brief +- Score: `4` + +### Screenshot and visual capture +- Matched keywords: screenshot, visual, screen +- Score: `3` + +### Citation and footnote clutter +- Matched keywords: citation, reference +- Score: `2` + +### Tone and specificity +- Matched keywords: copy, content +- Score: `2` + +### Tutorial quality +- Matched keywords: tutorial +- Score: `1` + +## Likely Output Mistakes + +- Tables can render as dense grids with weak hierarchy or poor mobile readability. +- Long bullets can make the output look complete while hiding the actual decision logic. +- Screenshots can be captured from the wrong state, wrong viewport, or wrong crop. +- Missing screenshots can cause the skill to invent visual references instead of declaring the gap. +- Footnote markers or dense citation notes can interrupt the reading flow. +- Evidence can be over-attached to obvious statements and under-attached to risky claims. + +## Output Constraints To Apply + +- Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets. +- Keep table cells short and move explanations below the table. +- Never invent a screenshot; state when visual evidence is missing. +- Record the source, viewport, and crop intent for any screenshot-dependent output. +- Attach citations only to claims that need evidence, not to every sentence. +- Group source notes at the end of a section when inline markers would hurt readability. + +## Self-Repair Checks + +- Preview whether each table still reads well when columns are narrow. +- Convert any table with paragraph-length cells into bullets or cards. +- Check that every screenshot reference points to a real provided or generated asset. +- Reword any visual instruction that depends on an unseen screen state. +- Remove decorative citations that do not support a material claim. +- Move repeated source explanations into one compact source note. + +## Reviewer Note + +Use this report before deepening the package and again before approving example outputs. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.json new file mode 100644 index 0000000..05acd53 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.json @@ -0,0 +1,146 @@ +{ + "skill_name": "geo-content-brief-skill", + "relevance": "prompt-heavy", + "primary_task_family": { + "key": "creative_generation", + "label": "Creative generation", + "matched_keywords": [ + "copy", + "content", + "内容" + ] + }, + "task_families": [ + { + "key": "creative_generation", + "label": "Creative generation", + "score": 3, + "matched_keywords": [ + "copy", + "content", + "内容" + ], + "role_guidance": "Use a taste-aware creator role with clear audience, tone, and originality boundaries.", + "task_guidance": "Generate variants, explain selection logic, and preserve the user's distinctive constraints.", + "format_guidance": "Return options with rationale, selection criteria, and refinement paths." + }, + { + "key": "execution_operation", + "label": "Execution operation", + "score": 3, + "matched_keywords": [ + "workflow", + "execute", + "执行" + ], + "role_guidance": "Use an operator role with explicit boundaries, inputs, outputs, and failure handling.", + "task_guidance": "Convert the job into ordered steps with validation checks and stop conditions.", + "format_guidance": "Return a runbook-like handoff with commands, checks, owners, and next actions when relevant." + }, + { + "key": "prompt_engineering", + "label": "Prompt engineering", + "score": 3, + "matched_keywords": [ + "prompt", + "role", + "format" + ], + "role_guidance": "Use a prompt engineer role only when role design materially improves execution.", + "task_guidance": "Map Role, Task, and Format into skill behavior rather than copying a large prompt template.", + "format_guidance": "Return a compact prompt contract plus tests, quality matrix, and usage notes." + }, + { + "key": "dialogue_interaction", + "label": "Dialogue interaction", + "score": 2, + "matched_keywords": [ + "dialogue", + "访谈" + ], + "role_guidance": "Use a conversational role that asks only high-leverage questions and remembers the user's goal.", + "task_guidance": "Clarify intent, resolve uncertainty, and converge toward a recommendation instead of a long option list.", + "format_guidance": "Return concise prompts, decision points, and reviewer-visible assumptions." + } + ], + "complexity": { + "band": "complex", + "score": 6, + "reason": "multiple inputs, constraints, or task families require tradeoff handling" + }, + "need_model": { + "explicit_need": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "implicit_need": "The reusable skill needs a stable role, task, and output contract rather than a one-off prompt.", + "scenario": "not yet explicit", + "user_level": "infer from examples and standards; ask only if it changes output depth", + "success_standard": "usable output with clear validation cues" + }, + "rtf_to_skill": { + "role": "Use a taste-aware creator role with clear audience, tone, and originality boundaries.", + "task": "Generate variants, explain selection logic, and preserve the user's distinctive constraints.", + "format": "Return options with rationale, selection criteria, and refinement paths." + }, + "quality_matrix": [ + { + "key": "completeness", + "label": "Completeness", + "score": 80, + "matched_signals": [ + "input", + "output", + "constraint", + "example", + "约束" + ], + "repair": "Name missing inputs, outputs, constraints, or success standards before deepening the package." + }, + { + "key": "clarity", + "label": "Clarity", + "score": 90, + "matched_signals": [ + "clear", + "specific" + ], + "repair": "Replace broad verbs with observable actions and define what done means." + }, + { + "key": "consistency", + "label": "Consistency", + "score": 90, + "matched_signals": [ + "boundary", + "边界" + ], + "repair": "Check that role, task, format, exclusions, and examples do not contradict each other." + }, + { + "key": "practicality", + "label": "Practicality", + "score": 95, + "matched_signals": [ + "execute", + "use", + "workflow", + "执行" + ], + "repair": "Add runnable steps, examples, or verification cues instead of abstract advice." + }, + { + "key": "specificity", + "label": "Specificity", + "score": 70, + "matched_signals": [], + "repair": "Anchor wording in the user's audience, domain nouns, and target outcome." + } + ], + "overall_quality_score": 85.0, + "self_repair_checks": [ + "Check explicit need, implicit need, scenario, user level, and success standard before deepening.", + "Map Role, Task, and Format into skill behavior, not decorative prompt labels.", + "Ask one focused clarification only when missing information changes the package boundary.", + "Add tests or examples for prompt-heavy behavior before treating it as reusable.", + "Keep prompt methodology in references and reports instead of bloating SKILL.md." + ], + "reviewer_note": "Use this profile when the package depends on prompt behavior, role design, output contracts, or conversation quality." +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.md new file mode 100644 index 0000000..5c2995a --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/prompt-quality-profile.md @@ -0,0 +1,94 @@ +# Prompt Quality Profile + +Skill: `geo-content-brief-skill` +Relevance: `prompt-heavy` +Overall quality score: `85.0/100` + +## Primary Task Family + +**Creative generation** +- Matched keywords: copy, content, 内容 + +## Complexity + +- Band: `complex` +- Score: `6` +- Reason: multiple inputs, constraints, or task families require tradeoff handling + +## Need Model + +- Explicit Need: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。 +- Implicit Need: The reusable skill needs a stable role, task, and output contract rather than a one-off prompt. +- Scenario: not yet explicit +- User Level: infer from examples and standards; ask only if it changes output depth +- Success Standard: usable output with clear validation cues + +## RTF To Skill Mapping + +- Role: Use a taste-aware creator role with clear audience, tone, and originality boundaries. +- Task: Generate variants, explain selection logic, and preserve the user's distinctive constraints. +- Format: Return options with rationale, selection criteria, and refinement paths. + +## Quality Matrix + +### Completeness — 80/100 +- Matched signals: input, output, constraint, example, 约束 +- Repair: Name missing inputs, outputs, constraints, or success standards before deepening the package. + +### Clarity — 90/100 +- Matched signals: clear, specific +- Repair: Replace broad verbs with observable actions and define what done means. + +### Consistency — 90/100 +- Matched signals: boundary, 边界 +- Repair: Check that role, task, format, exclusions, and examples do not contradict each other. + +### Practicality — 95/100 +- Matched signals: execute, use, workflow, 执行 +- Repair: Add runnable steps, examples, or verification cues instead of abstract advice. + +### Specificity — 70/100 +- Matched signals: none +- Repair: Anchor wording in the user's audience, domain nouns, and target outcome. + +## Matched Task Families + +### Creative generation +- Score: `3` +- Keywords: copy, content, 内容 +- Role: Use a taste-aware creator role with clear audience, tone, and originality boundaries. +- Task: Generate variants, explain selection logic, and preserve the user's distinctive constraints. +- Format: Return options with rationale, selection criteria, and refinement paths. + +### Execution operation +- Score: `3` +- Keywords: workflow, execute, 执行 +- Role: Use an operator role with explicit boundaries, inputs, outputs, and failure handling. +- Task: Convert the job into ordered steps with validation checks and stop conditions. +- Format: Return a runbook-like handoff with commands, checks, owners, and next actions when relevant. + +### Prompt engineering +- Score: `3` +- Keywords: prompt, role, format +- Role: Use a prompt engineer role only when role design materially improves execution. +- Task: Map Role, Task, and Format into skill behavior rather than copying a large prompt template. +- Format: Return a compact prompt contract plus tests, quality matrix, and usage notes. + +### Dialogue interaction +- Score: `2` +- Keywords: dialogue, 访谈 +- Role: Use a conversational role that asks only high-leverage questions and remembers the user's goal. +- Task: Clarify intent, resolve uncertainty, and converge toward a recommendation instead of a long option list. +- Format: Return concise prompts, decision points, and reviewer-visible assumptions. + +## Self-Repair Checks + +- Check explicit need, implicit need, scenario, user level, and success standard before deepening. +- Map Role, Task, and Format into skill behavior, not decorative prompt labels. +- Ask one focused clarification only when missing information changes the package boundary. +- Add tests or examples for prompt-heavy behavior before treating it as reusable. +- Keep prompt methodology in references and reports instead of bloating SKILL.md. + +## Reviewer Note + +Use this profile when the package depends on prompt behavior, role design, output contracts, or conversation quality. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.json new file mode 100644 index 0000000..5753465 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.json @@ -0,0 +1,36 @@ +{ + "skill_name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "scan_focus": [ + { + "label": "Execution pattern", + "reason": "There is deterministic logic in scripts, so compare how strong references separate prose from executable steps." + }, + { + "label": "Portability pattern", + "reason": "The package carries neutral metadata, so scan how good references preserve semantics across targets without forking source." + }, + { + "label": "Method pattern", + "reason": "Use the core job description as the anchor for comparison: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + } + ], + "external_references": [], + "user_references": [], + "local_constraints": [], + "borrow_plan": [ + "External benchmark first: let high-quality public references define the upper bound for method, structure, execution, or portability.", + "User references second: use them to understand taste, standards, and directional preferences without copying source phrasing.", + "Local fit third: use local assets only to detect naming conflicts, private dependencies, or compatibility constraints.", + "Borrow patterns, not prose: extract loops, boundaries, metadata, and operator flow without copying source-specific language.", + "Keep the package light: reject any borrowed pattern that increases context cost faster than it increases reliability." + ], + "priority_rule": "External benchmark objects set the pattern ceiling. User references refine taste and standards. Local files only calibrate fit, risk, and compatibility.", + "non_goals": [ + "Do not copy source prose or branding into the new skill.", + "Do not import gates that cost more context than they save.", + "Do not use benchmark scanning to justify scope creep.", + "Do not let local historical habits outrank stronger public benchmarks." + ] +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.md new file mode 100644 index 0000000..06b83aa --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-scan.md @@ -0,0 +1,54 @@ +# Reference Scan + +Skill: `geo-content-brief-skill` + +## Why This Step Exists + +Use a short benchmark pass before authoring the package in depth. External benchmark objects should define the pattern ceiling. Local files are used afterward only to calibrate fit, privacy, naming, and compatibility. + +## Current Skill Anchor + +- Title: `Geo Content Brief Skill` +- Description: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。 + +## Scan Focus + +- **Execution pattern**: There is deterministic logic in scripts, so compare how strong references separate prose from executable steps. +- **Portability pattern**: The package carries neutral metadata, so scan how good references preserve semantics across targets without forking source. +- **Method pattern**: Use the core job description as the anchor for comparison: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。 + +## Priority Rule + +- External benchmark objects set the pattern ceiling. User references refine taste and standards. Local files only calibrate fit, risk, and compatibility. + +## External Benchmark Objects + +- No explicit external benchmark objects recorded yet. +- Recommended: capture 2 to 5 external references at most. +- Suggested mix: one method reference, one structure reference, one execution or portability reference. + +## User-Supplied References + +- No user-supplied references recorded yet. +- Ask whether the user has a repo, product, page, workflow, or prompt example worth learning from. +- Treat these as pattern references only, not as text to be copied. + +## Local Fit Check + +- No local constraints recorded yet. +- Use this section for naming collisions, private dependencies, compatibility limits, or existing library conventions. + +## Borrow Plan + +- External benchmark first: let high-quality public references define the upper bound for method, structure, execution, or portability. +- User references second: use them to understand taste, standards, and directional preferences without copying source phrasing. +- Local fit third: use local assets only to detect naming conflicts, private dependencies, or compatibility constraints. +- Borrow patterns, not prose: extract loops, boundaries, metadata, and operator flow without copying source-specific language. +- Keep the package light: reject any borrowed pattern that increases context cost faster than it increases reliability. + +## Non-Goals + +- Do not copy source prose or branding into the new skill. +- Do not import gates that cost more context than they save. +- Do not use benchmark scanning to justify scope creep. +- Do not let local historical habits outrank stronger public benchmarks. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.json new file mode 100644 index 0000000..c998eab --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.json @@ -0,0 +1,171 @@ +{ + "skill_name": "geo-content-brief-skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "intent_confidence": { + "score": 10, + "band": "low", + "gate_passed": false + }, + "github_benchmarks": [], + "source_tracks": [ + { + "source_type": "official", + "name": "Official skill anatomy and context discipline", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "why_relevant": "This track matches: general fit." + }, + { + "source_type": "research", + "name": "Hypothesis-test-learn loop", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "why_relevant": "This track matches: general fit." + }, + { + "source_type": "principles", + "name": "Boundary-first design", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "why_relevant": "This track matches: general fit." + } + ], + "synthesis": { + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "avoid_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier.", + "Do not expand execution assets until route boundaries stay clean." + ], + "quality_risers": [ + "Use GitHub repositories for concrete package and workflow patterns.", + "Use curated official or commercial tracks for entrypoint and operator ergonomics.", + "Use research tracks to justify the smallest evaluation loop that still catches regressions.", + "Use principle tracks to keep the package small, boundary-aware, and outcome-driven." + ], + "pattern_gate": { + "threshold": 2, + "source_count": 3, + "accepted": [ + { + "name": "Official skill anatomy and context discipline", + "source_type": "official", + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": false, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "boundary" + ], + "missing": [ + "distinctiveness" + ], + "score": 3 + }, + { + "name": "Hypothesis-test-learn loop", + "source_type": "research", + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": false, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "boundary" + ], + "missing": [ + "distinctiveness" + ], + "score": 3 + }, + { + "name": "Boundary-first design", + "source_type": "principles", + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": false, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "boundary" + ], + "missing": [ + "distinctiveness" + ], + "score": 3 + } + ], + "deferred": [], + "summary": "3 accepted, 0 deferred using threshold 2/4." + }, + "conflicts": [ + { + "key": "lightweight_vs_governance", + "summary": "The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_preference": "lightweight or speed-first", + "benchmark_pressure": "governance or evaluation-heavy patterns" + } + ], + "recommendation": { + "summary": "Start by borrowing this pattern: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. Avoid this for the first pass: Do not let packaging or platform concerns swallow the core job boundary.", + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed." + ], + "avoid_for_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier." + ], + "why": "There is a real design conflict to resolve: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_decision_required": true + }, + "visibility": { + "mode": "explicit", + "user_decision_required": true, + "reasons": [ + "intent_uncertain", + "design_conflict" + ], + "user_note": "Surface the recommendation because intent is still settling or there is a real design conflict that needs a user call.", + "reviewer_note": "Keep the full benchmark and synthesis evidence visible for authors and reviewers." + }, + "decision_prompt": "Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call.", + "source_mix": { + "github_benchmarks": 0, + "curated_tracks": 3, + "user_references": 0 + } + } +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.md new file mode 100644 index 0000000..8824a75 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/reference-synthesis.md @@ -0,0 +1,81 @@ +# Reference Synthesis + +Skill: `geo-content-brief-skill` +- Description: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。 +- Intent confidence: `10/100` (`low`) + +## Live GitHub Benchmarks + +- No live GitHub benchmarks are attached yet. + +## Curated World-Class Pattern Tracks + +### Official skill anatomy and context discipline +- Type: `official` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: general fit. +- Borrow: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. +- Avoid: Do not let packaging or platform concerns swallow the core job boundary. + +### Hypothesis-test-learn loop +- Type: `research` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: general fit. +- Borrow: Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. +- Avoid: Do not create experimental overhead that exceeds the skill's real risk tier. + +### Boundary-first design +- Type: `principles` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: general fit. +- Borrow: Borrow the discipline of defining what the skill should not own before growing the package. +- Avoid: Do not expand execution assets until route boundaries stay clean. + +## Borrow Now + +- Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. +- Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. +- Borrow the discipline of defining what the skill should not own before growing the package. + +## Avoid Now + +- Do not let packaging or platform concerns swallow the core job boundary. +- Do not create experimental overhead that exceeds the skill's real risk tier. +- Do not expand execution assets until route boundaries stay clean. + +## Pattern Gate + +- Summary: 3 accepted, 0 deferred using threshold 2/4. +- Acceptance threshold: `2/4` +- Accepted patterns: + - **Official skill anatomy and context discipline**: 3/4 (recurrence, generativity, boundary) + - **Hypothesis-test-learn loop**: 3/4 (recurrence, generativity, boundary) + - **Boundary-first design**: 3/4 (recurrence, generativity, boundary) + +## Default Recommendation + +- Summary: Start by borrowing this pattern: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. Avoid this for the first pass: Do not let packaging or platform concerns swallow the core job boundary. +- Why: There is a real design conflict to resolve: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure. +- User decision required: `True` + +## Visibility Mode + +- Mode: `explicit` +- Reasons: intent_uncertain, design_conflict +- User note: Surface the recommendation because intent is still settling or there is a real design conflict that needs a user call. +- Reviewer note: Keep the full benchmark and synthesis evidence visible for authors and reviewers. + +## Conflict Check + +- **lightweight_vs_governance**: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure. + +## Quality Lift Thesis + +- Use GitHub repositories for concrete package and workflow patterns. +- Use curated official or commercial tracks for entrypoint and operator ergonomics. +- Use research tracks to justify the smallest evaluation loop that still catches regressions. +- Use principle tracks to keep the package small, boundary-aware, and outcome-driven. + +## Decision Prompt + +Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.html b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.html new file mode 100644 index 0000000..a1d72d2 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.html @@ -0,0 +1,720 @@ + + + + + + Geo Content Brief Skill Review Studio 2.0 + + + +

+
+
+
Review Studio 2.0
+

Geo Content Brief Skill

+

将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。

+
+ 审查结论 + review + Score 67/100 +
+
+ +
+

核心指标

+
Skill IR2.0.0

5 targets in platform-neutral contract

Compiler5/5

target contracts compiled from Skill IR

Output Deltan/a

0 cases; 0 file-backed

Exec Runs0

command 0; model 0; recorded 0

Blind A/B0

review pairs hide baseline vs with-skill labels

Review Kit0/0

pending 0; answer key hidden

Review A/B0/0

adjudication decisions; pending 0

Public Claimblocked

0 blockers; local reproducible false

Blueprint0/0

2.0 coverage; evidence pending 0

Runtime0/0

target conformance pass rate

Perm Probe0/0

0 native; 0 installer-enforced

Trust0

0 scripts scanned; secrets found

Py Compat0

0 files scanned for Python 3.11

Arch Debt0

0 largest lines; 0 CLI handlers

Atlas0

0 scanned skills; route collisions

Driftno-data

0 metadata events; 0 missed triggers

Waivers0

0 gates covered; human risk decisions

Intake0/0

0 valid submissions; 0 invalid

Claim Guard0

0 public surfaces scanned

Notes0/0

0 open blocker annotations

Registryn/a

0 targets; no license license

Archiven/a

0 zip entries; package verification

Installn/a

0 adapters; 0 permissions enforced; 0 permission failures

Upgraden/a

declared n/a; 0 breaking changes

+
+ +
+

审查闸门

+
关注

意图画布

intent confidence 10/100; Pause before deep authoring and close the highest-leverage gaps first.

reports/intent-confidence.json 证据
关注

触发实验

route scorecard is missing; run route-scorecard before release review

reports/route_scorecard.json 证据
关注

输出实验

output eval scorecard is missing; generate it before production review

reports/output_quality_scorecard.json 证据
关注

上下文

initial load n/a/n/a; deferred n/a/n/a; top deferred none; resource governance unknown; quality density n/a

reports/context_budget.json 证据
关注

运行矩阵

runtime conformance matrix is missing

reports/conformance_matrix.json 证据
关注

信任报告

security trust report is missing

reports/security_trust_report.json 证据
关注

Python 兼容

Python compatibility report is missing

reports/python_compatibility.json 证据
通过

架构维护

architecture maintainability report is optional until code surface grows

reports/architecture_maintainability.json 证据
关注

权限批准

permission governance evidence is missing because trust report is missing

reports/security_trust_report.json + security/permission_policy.json 证据
关注

权限探针

runtime permission probe report is missing

reports/runtime_permission_probes.json 证据
关注

组合治理

skill atlas is missing; portfolio-level conflicts are unknown

reports/skill_atlas.json 证据
关注

运营回路

0 metadata events; adoption 0; missed 0; bad-output 0; risk no-data

reports/adoption_drift_report.json 证据
关注

人工批准

0 active waivers; 11 warning gates still need reviewer decision

reports/review_waivers.json 证据
通过

世界证据

world-class evidence ledger is optional until governed or public world-class readiness is claimed

reports/world_class_evidence_ledger.json 证据
通过

注册审计

registry audit is optional until team distribution is required

reports/registry_audit.json + reports/install_simulation.json 证据
关注

发布路线

promotion decisions are missing; release notes need reviewer confirmation

reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md 证据
+
+ +
+

阻断事项

无。

+

关注事项

  • 意图画布intent confidence 10/100; Pause before deep authoring and close the highest-leverage gaps first.
  • 触发实验route scorecard is missing; run route-scorecard before release review
  • 输出实验output eval scorecard is missing; generate it before production review
  • 上下文initial load n/a/n/a; deferred n/a/n/a; top deferred none; resource governance unknown; quality density n/a
  • 运行矩阵runtime conformance matrix is missing
  • 信任报告security trust report is missing
  • Python 兼容Python compatibility report is missing
  • 权限批准permission governance evidence is missing because trust report is missing
  • 权限探针runtime permission probe report is missing
  • 组合治理skill atlas is missing; portfolio-level conflicts are unknown
  • 运营回路0 metadata events; adoption 0; missed 0; bad-output 0; risk no-data
  • 人工批准0 active waivers; 11 warning gates still need reviewer decision
  • 发布路线promotion decisions are missing; release notes need reviewer confirmation
+
+ +
+

修复动作

+
关注

意图画布

收紧真实任务、输入、输出、排除项和成功标准。

低 intent confidence 会让后续 Skill IR、输出评测和 Review Studio 结论建立在模糊意图上。
修复位置
reports/intent-dialogue.md + reports/intent-confidence.md
验证命令
python3 scripts/yao.py intent-confidence .
关注

触发实验

修正 route scorecard 中的误触发、漏触发或 ambiguous case。

触发错误会让正确 Skill 失活,或让相邻 Skill 被错误调用。
修复位置
SKILL.md frontmatter description + evals/*/trigger_cases.json
验证命令
python3 scripts/build_confusion_matrix.py
  • SKILL.md:3frontmatter description · source
  • evals/trigger_cases.json · missingtrigger eval cases · eval
  • reports/route_scorecard.md · missingroute scorecard · report
关注

输出实验

补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。

没有输出质量和人工盲评证据时,Skill 只能证明会触发,不能证明输出真的更好且经得起审查。
修复位置
evals/output/cases.jsonl + reports/output_quality_scorecard.md + reports/output_review_kit.html + reports/output_review_adjudication.md
验证命令
python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review
  • evals/output/cases.jsonl · missingoutput eval cases · eval
  • reports/output_quality_scorecard.md · missingoutput scorecard · report
  • reports/output_execution_runs.md · missingoutput execution runs · report
  • reports/output_blind_review_pack.md · missingblind A/B review pack · report
  • reports/output_review_kit.html · missingreviewer cockpit · report
  • reports/output_review_adjudication.md · missingreview adjudication · report
关注

上下文

压缩或拆分高成本 deferred resources,保留最小可路由上下文。

初始加载可以安全,但后续 references、scripts、evals 体量过大时,reviewer 仍需要看到维护和读取成本。
修复位置
SKILL.md + references/ + scripts/ + evals/
验证命令
python3 scripts/render_context_reports.py
  • SKILL.md:1entrypoint · source
  • reports/context_budget.md · missingcontext budget · report
  • scripts/resource_boundary_check.py · missingresource boundary checker · source
  • references/skill-engineering-method.md · missingskill engineering method · method
关注

运行矩阵

修复目标端结构、metadata、相对路径、fallback 或 adapter target 声明。

runtime conformance 失败意味着包可能被目标客户端错误加载或静默降级。
修复位置
agents/interface.yaml + reports/conformance_matrix.md
验证命令
python3 scripts/run_conformance_suite.py .
  • agents/interface.yaml:7portable interface · source
  • reports/conformance_matrix.md · missingconformance matrix · report
关注

信任报告

处理脚本 help surface、依赖 pin、network policy、secret 和权限声明。

团队分发时,脚本和依赖是主要供应链风险面,warning 必须有明确处置。
修复位置
reports/security_trust_report.md + security/*.md + scripts/
验证命令
python3 scripts/trust_check.py .
  • reports/security_trust_report.md · missingtrust report · report
  • security/script_policy.md · missingscript policy · policy
  • security/network_policy.md · missingnetwork policy · policy
关注

Python 兼容

修复 Python 3.11 语法兼容问题,尤其是 f-string 表达式内的反斜杠转义。

目标运行环境可能仍停留在 Python 3.11,语法漂移会让 CLI、报告生成和 CI 在发布后直接失败。
修复位置
reports/python_compatibility.md + scripts/*.py + tests/*.py
验证命令
python3 scripts/yao.py python-compat .
  • reports/python_compatibility.md · missingPython compatibility · report
  • scripts/python_compat_check.py · missingcompatibility checker · source
  • .github/workflows/test.yml · missingCI test workflow · ci
关注

权限批准

补齐高权限能力的 reviewer、scope、reason、expires_at 和目标端 enforcement 说明。

权限契约只有在批准人、有效期和目标端处置方式明确时,才能支撑 governed release。
修复位置
security/permission_policy.json + security/permission_policy.md
验证命令
python3 scripts/trust_check.py .
  • security/permission_policy.json · missingpermission approvals · policy
  • security/permission_policy.md · missingpermission method · policy
reports/security_trust_report.json + security/permission_policy.json 打开证据
关注

权限探针

生成并修复目标包的 runtime permission probe 报告。

目标端即使只能提供 metadata fallback,也必须明确 native enforcement 缺口、表示位置和 operator note。
修复位置
dist/targets/*/adapter.json + reports/runtime_permission_probes.md
验证命令
python3 scripts/probe_runtime_permissions.py . --package-dir dist
  • reports/runtime_permission_probes.md · missingruntime permission probes · report
  • dist/targets/openai/adapter.json · missingOpenAI adapter · package
  • dist/targets/claude/adapter.json · missingClaude adapter · package
  • dist/targets/generic/adapter.json · missinggeneric adapter · package
关注

组合治理

处理 portfolio 里的路由冲突、owner 缺口、stale skill 和重复能力。

单个 Skill 质量很高仍可能在团队 skill library 中被相邻 Skill 冲突削弱。
修复位置
reports/skill_atlas.html + skill_atlas/catalog.json
验证命令
python3 scripts/build_skill_atlas.py --workspace-root .
  • skill_atlas/catalog.json · missingskill atlas catalog · atlas
  • skill_atlas/policy.json · missingatlas scope policy · policy
  • reports/skill_atlas.html · missingskill atlas report · report
关注

运营回路

记录 metadata-only 使用事件,或明确当前 release 缺少真实使用信号。

没有运营回路时,reviewer 无法判断采用率、误触发、坏输出和 review overdue 的真实影响。
修复位置
reports/adoption_drift_report.md
验证命令
python3 scripts/render_adoption_drift_report.py . --record-event skill_activation --activation-type explicit --outcome accepted
关注

人工批准

对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。

warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。
修复位置
reports/review_waivers.md
验证命令
python3 scripts/render_review_waivers.py .
关注

发布路线

确认 promotion、upgrade diff、breaking changes、migration guide 和 known limitations。

发布说明不完整会让使用者无法判断升级风险和迁移动作。
修复位置
reports/upgrade_check.md + docs/migration-v2.md
验证命令
python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json
  • reports/upgrade_check.md · missingupgrade check · report
  • docs/migration-v2.md · missingmigration guide · docs
  • reports/promotion_decisions.md · missingpromotion decisions · report
reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md 打开证据
+
+ +
+

审查批注

+

当前批注:0 条批注;开放 0;阻断 0

+

当前没有 reviewer 批注。

+
+ +
+
+

意图画布

+

将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。

+
+
+

证据路径

+
  • skill_overviewreports/skill-overview.html
  • review_viewerreports/review-viewer.html
  • output_evalreports/output_quality_scorecard.md
  • output_executionreports/output_execution_runs.md
  • output_blind_reviewreports/output_blind_review_pack.md
  • output_review_kitreports/output_review_kit.md
  • output_review_kit_htmlreports/output_review_kit.html
  • output_review_decisionsreports/output_review_decisions.json
  • output_review_adjudicationreports/output_review_adjudication.md
  • benchmark_reproducibilityreports/benchmark_reproducibility.md
  • skill_os2_coveragereports/skill_os2_coverage.md
  • runtime_conformancereports/conformance_matrix.md
  • trust_reportreports/security_trust_report.md
  • python_compatibilityreports/python_compatibility.md
  • architecture_maintainabilityreports/architecture_maintainability.md
  • permission_policysecurity/permission_policy.md
  • runtime_permissionsreports/runtime_permission_probes.md
  • skill_atlasreports/skill_atlas.html
  • compiled_targetsreports/compiled_targets.md
  • adoption_driftreports/adoption_drift_report.md
  • review_waiversreports/review_waivers.md
  • review_annotationsreports/review_annotations.md
  • world_class_evidence_planreports/world_class_evidence_plan.md
  • world_class_evidence_ledgerreports/world_class_evidence_ledger.md
  • world_class_evidence_intakereports/world_class_evidence_intake.md
  • world_class_submission_reviewreports/world_class_submission_review.md
  • world_class_operator_runbookreports/world_class_operator_runbook.md
  • world_class_operator_runbook_htmlreports/world_class_operator_runbook.html
  • world_class_claim_guardreports/world_class_claim_guard.md
  • registry_auditreports/registry_audit.md
  • package_verificationreports/package_verification.md
  • install_simulationreports/install_simulation.md
  • upgrade_checkreports/upgrade_check.md
  • migrationdocs/migration-v2.md
  • skill_irreports/skill-ir.json
+
+
+ +
+

触发实验

route scorecard is missing; run route-scorecard before release review

+

组合治理

skill atlas summary missing

+
+ +
+

输出实验

output eval scorecard missing

+

执行证据

output execution report missing

+
+ +
+

盲评包

blind A/B review pack missing

+

审定报告

review adjudication report missing

+
+ +

评审清单

先打开 reports/output_review_kit.md;每张卡片对应一个 blind A/B case,说明当前是否待判、答案是否仍隐藏、应填写的决策文件、有效字段和复跑命令。

当前没有输出评审操作清单。

+
+

发布标准

Governed 和 Library 至少需要 5 个 output eval cases,并覆盖 file-backed、near-neighbor、boundary case、execution evidence 和 blind A/B review pack。

+

人工结论

没有 reviewer 决策时只显示 pending;只有真实决策文件会进入一致率和分歧统计。

+
+ +
+

评审方式

先打开 reports/output_review_kit.html 做盲评,填入 reports/output_review_decisions.json,再用 reports/output_review_adjudication.md 核对答案 key。

+

运行方式

reports/output_execution_runs.md 会标明 recorded fixture、command run 或 model run;只有 provider runner 返回 model metadata 时才算 model-executed。

+
+ +
+

运行矩阵

runtime conformance matrix missing

+

目标编译

目标数
5
通过数
5
Warn Count
0
Block Count
0
失败数
0
+
+ +
+

上下文

initial load n/a/n/a; deferred n/a/n/a; top deferred none; resource governance unknown; quality density n/a

+

编译证据

Review reports/compiled_targets.md before packaging to inspect target adapter modes, generated files, preserved semantics, warnings, and unsupported features.

+
+ +
+

信任报告

security trust report missing

+

安全边界

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

+
+ +
+

Python 兼容

python compatibility report missing

+

解释器边界

CI 和发布审查以 Python 3.11 兼容为底线;本地更高版本允许的新语法不能绕过兼容门禁。

+
+ +
+

权限批准

permission governance evidence is missing because trust report is missing

+

批准策略

高权限能力需要 reviewer、scope、reason、expires_at 和 openai/claude/generic 目标端 enforcement 说明。

+
+ +
+

权限探针

runtime permission probe report is missing

+

运行边界

runtime permission probe summary missing

+
+ +
+

组合治理

skill atlas is missing; portfolio-level conflicts are unknown

+

下一动作

优先处理真实 portfolio 中的 duplicate names、stale skills、owner gaps,再用运营回路判断真实影响。

+
+ +
+

运营回路

0 metadata events; adoption 0; missed 0; bad-output 0; risk no-data

+

漂移信号

事件数
0
采用率
0
漏触发
0
Bad Output Count
0
风险带
no-data
+
+ +
+

人工批准

+

warning 可以被有边界地接受,但必须写入 reviewer、理由、范围和到期时间;blocker 与 world-class 完成证据不能通过 waiver 变成通过。

+
+

批准概况

0 active waivers; 11 warning gates still need reviewer decision

+

批准台账

Waiver Count
0
Active Count
0
Expired Count
0
Invalid Count
0
覆盖 Gate
0
+
+

批准候选

+

当前没有需要 reviewer 处理的批准候选。

+
+ +
+

世界证据

+

这里列出每个 world-class 证据项的当前状态、完成定义、证据来源、隐私约束和下一步;计划、metadata fallback、待评审和本地命令不会被当成完成证据。

+

当前没有 world-class 证据条目。

+
+ +
+

提交清单

+

每张卡片给出模板、提交文件、准备命令、校验命令、收集要求、通过条件和隐私边界;只有真实 provider、真人、原生权限或真实客户端证据通过 intake 后,才进入 ledger review。

+

当前没有 world-class 证据操作清单。

+
+ +
+

蓝图覆盖

Skill OS 2.0 blueprint coverage missing

+

覆盖边界

蓝图覆盖只证明 2.0 模块、建议 PR、脚本、报告和测试在本地闭环;public world-class 仍以 world-class evidence ledger 的真人和外部证据为准。

+
+ +
+

公开声明

benchmark reproducibility report missing

+

声明阻断

无公开声明阻断。

+
+ +
+

世界证据

world-class evidence ledger is optional until governed or public world-class readiness is claimed

+

证据台账

world-class evidence ledger missing

+
+ +
+

证据入口

world-class evidence intake missing

+

入口边界

intake 只校验证据包格式、来源、隐私和反过度声明;只有 ledger 看到真实 provider、真人、原生权限或真实客户端结果后,world-class 才能进入完成审计。

+
+ +
+

声明守卫

world-class claim guard missing

+

声明边界

claim guard 扫描 README、docs 和 reports 中的完成态表述;ledger 未 ready 时,任何英文完成断言、true 状态声明或中文完成态都会阻断发布审查。

+
+ +
+

注册审计

registry audit is optional until team distribution is required

+

包体元数据

registry package metadata missing

+
+ +
+

发布路线

promotion decisions are missing; release notes need reviewer confirmation

+

包体验证

package verification missing

+
+ +
+

安装模拟

install simulation missing

+

权限覆盖

安装模拟会读取解压后的 target adapter,并按 declared_capabilities 核对 security/permission_policy.json 中的有效批准、证据、过期时间和目标端 enforcement 说明。

+
+
+ + diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.json new file mode 100644 index 0000000..a66a20d --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-studio.json @@ -0,0 +1,4450 @@ +{ + "schema_version": "2.0", + "ok": true, + "skill_dir": ".", + "summary": { + "decision": "review", + "world_class_score": 67, + "gate_count": 16, + "blocker_count": 0, + "warning_count": 13, + "action_count": 13, + "annotation_count": 0, + "open_annotation_count": 0, + "open_annotation_blocker_count": 0, + "open_annotation_warning_count": 0 + }, + "gates": [ + { + "key": "intent-canvas", + "label": "意图画布", + "status": "warn", + "detail": "intent confidence 10/100; Pause before deep authoring and close the highest-leverage gaps first.", + "evidence": "reports/intent-confidence.json", + "link": "intent-confidence.md" + }, + { + "key": "trigger-lab", + "label": "触发实验", + "status": "warn", + "detail": "route scorecard is missing; run route-scorecard before release review", + "evidence": "reports/route_scorecard.json", + "link": "route_scorecard.md" + }, + { + "key": "output-lab", + "label": "输出实验", + "status": "warn", + "detail": "output eval scorecard is missing; generate it before production review", + "evidence": "reports/output_quality_scorecard.json", + "link": "output_quality_scorecard.md" + }, + { + "key": "context-budget", + "label": "上下文", + "status": "warn", + "detail": "initial load n/a/n/a; deferred n/a/n/a; top deferred none; resource governance unknown; quality density n/a", + "evidence": "reports/context_budget.json", + "link": "context_budget.md" + }, + { + "key": "runtime-matrix", + "label": "运行矩阵", + "status": "warn", + "detail": "runtime conformance matrix is missing", + "evidence": "reports/conformance_matrix.json", + "link": "conformance_matrix.md" + }, + { + "key": "trust-report", + "label": "信任报告", + "status": "warn", + "detail": "security trust report is missing", + "evidence": "reports/security_trust_report.json", + "link": "security_trust_report.md" + }, + { + "key": "python-compat", + "label": "Python 兼容", + "status": "warn", + "detail": "Python compatibility report is missing", + "evidence": "reports/python_compatibility.json", + "link": "python_compatibility.md" + }, + { + "key": "architecture-maintainability", + "label": "架构维护", + "status": "pass", + "detail": "architecture maintainability report is optional until code surface grows", + "evidence": "reports/architecture_maintainability.json", + "link": "architecture_maintainability.md" + }, + { + "key": "permission-gates", + "label": "权限批准", + "status": "warn", + "detail": "permission governance evidence is missing because trust report is missing", + "evidence": "reports/security_trust_report.json + security/permission_policy.json", + "link": "../security/permission_policy.md" + }, + { + "key": "permission-runtime", + "label": "权限探针", + "status": "warn", + "detail": "runtime permission probe report is missing", + "evidence": "reports/runtime_permission_probes.json", + "link": "runtime_permission_probes.md" + }, + { + "key": "skill-atlas", + "label": "组合治理", + "status": "warn", + "detail": "skill atlas is missing; portfolio-level conflicts are unknown", + "evidence": "reports/skill_atlas.json", + "link": "skill_atlas.html" + }, + { + "key": "operations-loop", + "label": "运营回路", + "status": "warn", + "detail": "0 metadata events; adoption 0; missed 0; bad-output 0; risk no-data", + "evidence": "reports/adoption_drift_report.json", + "link": "adoption_drift_report.md" + }, + { + "key": "review-waivers", + "label": "人工批准", + "status": "warn", + "detail": "0 active waivers; 11 warning gates still need reviewer decision", + "evidence": "reports/review_waivers.json", + "link": "review_waivers.md" + }, + { + "key": "world-class-evidence", + "label": "世界证据", + "status": "pass", + "detail": "world-class evidence ledger is optional until governed or public world-class readiness is claimed", + "evidence": "reports/world_class_evidence_ledger.json", + "link": "world_class_evidence_ledger.md" + }, + { + "key": "registry-audit", + "label": "注册审计", + "status": "pass", + "detail": "registry audit is optional until team distribution is required", + "evidence": "reports/registry_audit.json + reports/install_simulation.json", + "link": "registry_audit.md" + }, + { + "key": "release-notes", + "label": "发布路线", + "status": "warn", + "detail": "promotion decisions are missing; release notes need reviewer confirmation", + "evidence": "reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md", + "link": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/docs/migration-v2.md" + } + ], + "blockers": [], + "warnings": [ + { + "key": "intent-canvas", + "label": "意图画布", + "status": "warn", + "detail": "intent confidence 10/100; Pause before deep authoring and close the highest-leverage gaps first.", + "evidence": "reports/intent-confidence.json", + "link": "intent-confidence.md" + }, + { + "key": "trigger-lab", + "label": "触发实验", + "status": "warn", + "detail": "route scorecard is missing; run route-scorecard before release review", + "evidence": "reports/route_scorecard.json", + "link": "route_scorecard.md" + }, + { + "key": "output-lab", + "label": "输出实验", + "status": "warn", + "detail": "output eval scorecard is missing; generate it before production review", + "evidence": "reports/output_quality_scorecard.json", + "link": "output_quality_scorecard.md" + }, + { + "key": "context-budget", + "label": "上下文", + "status": "warn", + "detail": "initial load n/a/n/a; deferred n/a/n/a; top deferred none; resource governance unknown; quality density n/a", + "evidence": "reports/context_budget.json", + "link": "context_budget.md" + }, + { + "key": "runtime-matrix", + "label": "运行矩阵", + "status": "warn", + "detail": "runtime conformance matrix is missing", + "evidence": "reports/conformance_matrix.json", + "link": "conformance_matrix.md" + }, + { + "key": "trust-report", + "label": "信任报告", + "status": "warn", + "detail": "security trust report is missing", + "evidence": "reports/security_trust_report.json", + "link": "security_trust_report.md" + }, + { + "key": "python-compat", + "label": "Python 兼容", + "status": "warn", + "detail": "Python compatibility report is missing", + "evidence": "reports/python_compatibility.json", + "link": "python_compatibility.md" + }, + { + "key": "permission-gates", + "label": "权限批准", + "status": "warn", + "detail": "permission governance evidence is missing because trust report is missing", + "evidence": "reports/security_trust_report.json + security/permission_policy.json", + "link": "../security/permission_policy.md" + }, + { + "key": "permission-runtime", + "label": "权限探针", + "status": "warn", + "detail": "runtime permission probe report is missing", + "evidence": "reports/runtime_permission_probes.json", + "link": "runtime_permission_probes.md" + }, + { + "key": "skill-atlas", + "label": "组合治理", + "status": "warn", + "detail": "skill atlas is missing; portfolio-level conflicts are unknown", + "evidence": "reports/skill_atlas.json", + "link": "skill_atlas.html" + }, + { + "key": "operations-loop", + "label": "运营回路", + "status": "warn", + "detail": "0 metadata events; adoption 0; missed 0; bad-output 0; risk no-data", + "evidence": "reports/adoption_drift_report.json", + "link": "adoption_drift_report.md" + }, + { + "key": "review-waivers", + "label": "人工批准", + "status": "warn", + "detail": "0 active waivers; 11 warning gates still need reviewer decision", + "evidence": "reports/review_waivers.json", + "link": "review_waivers.md" + }, + { + "key": "release-notes", + "label": "发布路线", + "status": "warn", + "detail": "promotion decisions are missing; release notes need reviewer confirmation", + "evidence": "reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md", + "link": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/docs/migration-v2.md" + } + ], + "review_actions": [ + { + "gate_key": "intent-canvas", + "label": "意图画布", + "status": "warn", + "priority": "warning", + "summary": "收紧真实任务、输入、输出、排除项和成功标准。", + "why": "低 intent confidence 会让后续 Skill IR、输出评测和 Review Studio 结论建立在模糊意图上。", + "source_fix": "reports/intent-dialogue.md + reports/intent-confidence.md", + "source_refs": [ + { + "path": "reports/intent-dialogue.md", + "label": "intent dialogue", + "kind": "report", + "line": 1, + "exists": true, + "link": "intent-dialogue.md" + }, + { + "path": "reports/intent-confidence.md", + "label": "intent confidence", + "kind": "report", + "line": 1, + "exists": true, + "link": "intent-confidence.md" + } + ], + "evidence": "reports/intent-confidence.json", + "evidence_link": "intent-confidence.md", + "verification_command": "python3 scripts/yao.py intent-confidence ." + }, + { + "gate_key": "trigger-lab", + "label": "触发实验", + "status": "warn", + "priority": "warning", + "summary": "修正 route scorecard 中的误触发、漏触发或 ambiguous case。", + "why": "触发错误会让正确 Skill 失活,或让相邻 Skill 被错误调用。", + "source_fix": "SKILL.md frontmatter description + evals/*/trigger_cases.json", + "source_refs": [ + { + "path": "SKILL.md", + "label": "frontmatter description", + "kind": "source", + "line": 3, + "exists": true, + "link": "../SKILL.md" + }, + { + "path": "evals/trigger_cases.json", + "label": "trigger eval cases", + "kind": "eval", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/route_scorecard.md", + "label": "route scorecard", + "kind": "report", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/route_scorecard.json", + "evidence_link": "route_scorecard.md", + "verification_command": "python3 scripts/build_confusion_matrix.py" + }, + { + "gate_key": "output-lab", + "label": "输出实验", + "status": "warn", + "priority": "warning", + "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_refs": [ + { + "path": "evals/output/cases.jsonl", + "label": "output eval cases", + "kind": "eval", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/output_quality_scorecard.md", + "label": "output scorecard", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/output_execution_runs.md", + "label": "output execution runs", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/output_blind_review_pack.md", + "label": "blind A/B review pack", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/output_review_kit.html", + "label": "reviewer cockpit", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/output_review_adjudication.md", + "label": "review adjudication", + "kind": "report", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/output_quality_scorecard.json", + "evidence_link": "output_quality_scorecard.md", + "verification_command": "python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review" + }, + { + "gate_key": "context-budget", + "label": "上下文", + "status": "warn", + "priority": "warning", + "summary": "压缩或拆分高成本 deferred resources,保留最小可路由上下文。", + "why": "初始加载可以安全,但后续 references、scripts、evals 体量过大时,reviewer 仍需要看到维护和读取成本。", + "source_fix": "SKILL.md + references/ + scripts/ + evals/", + "source_refs": [ + { + "path": "SKILL.md", + "label": "entrypoint", + "kind": "source", + "line": 1, + "exists": true, + "link": "../SKILL.md" + }, + { + "path": "reports/context_budget.md", + "label": "context budget", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "scripts/resource_boundary_check.py", + "label": "resource boundary checker", + "kind": "source", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "references/skill-engineering-method.md", + "label": "skill engineering method", + "kind": "method", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/context_budget.json", + "evidence_link": "context_budget.md", + "verification_command": "python3 scripts/render_context_reports.py" + }, + { + "gate_key": "runtime-matrix", + "label": "运行矩阵", + "status": "warn", + "priority": "warning", + "summary": "修复目标端结构、metadata、相对路径、fallback 或 adapter target 声明。", + "why": "runtime conformance 失败意味着包可能被目标客户端错误加载或静默降级。", + "source_fix": "agents/interface.yaml + reports/conformance_matrix.md", + "source_refs": [ + { + "path": "agents/interface.yaml", + "label": "portable interface", + "kind": "source", + "line": 7, + "exists": true, + "link": "../agents/interface.yaml" + }, + { + "path": "reports/conformance_matrix.md", + "label": "conformance matrix", + "kind": "report", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/conformance_matrix.json", + "evidence_link": "conformance_matrix.md", + "verification_command": "python3 scripts/run_conformance_suite.py ." + }, + { + "gate_key": "trust-report", + "label": "信任报告", + "status": "warn", + "priority": "warning", + "summary": "处理脚本 help surface、依赖 pin、network policy、secret 和权限声明。", + "why": "团队分发时,脚本和依赖是主要供应链风险面,warning 必须有明确处置。", + "source_fix": "reports/security_trust_report.md + security/*.md + scripts/", + "source_refs": [ + { + "path": "reports/security_trust_report.md", + "label": "trust report", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "security/script_policy.md", + "label": "script policy", + "kind": "policy", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "security/network_policy.md", + "label": "network policy", + "kind": "policy", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/security_trust_report.json", + "evidence_link": "security_trust_report.md", + "verification_command": "python3 scripts/trust_check.py ." + }, + { + "gate_key": "python-compat", + "label": "Python 兼容", + "status": "warn", + "priority": "warning", + "summary": "修复 Python 3.11 语法兼容问题,尤其是 f-string 表达式内的反斜杠转义。", + "why": "目标运行环境可能仍停留在 Python 3.11,语法漂移会让 CLI、报告生成和 CI 在发布后直接失败。", + "source_fix": "reports/python_compatibility.md + scripts/*.py + tests/*.py", + "source_refs": [ + { + "path": "reports/python_compatibility.md", + "label": "Python compatibility", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "scripts/python_compat_check.py", + "label": "compatibility checker", + "kind": "source", + "line": null, + "exists": false, + "link": "" + }, + { + "path": ".github/workflows/test.yml", + "label": "CI test workflow", + "kind": "ci", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/python_compatibility.json", + "evidence_link": "python_compatibility.md", + "verification_command": "python3 scripts/yao.py python-compat ." + }, + { + "gate_key": "permission-gates", + "label": "权限批准", + "status": "warn", + "priority": "warning", + "summary": "补齐高权限能力的 reviewer、scope、reason、expires_at 和目标端 enforcement 说明。", + "why": "权限契约只有在批准人、有效期和目标端处置方式明确时,才能支撑 governed release。", + "source_fix": "security/permission_policy.json + security/permission_policy.md", + "source_refs": [ + { + "path": "security/permission_policy.json", + "label": "permission approvals", + "kind": "policy", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "security/permission_policy.md", + "label": "permission method", + "kind": "policy", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/security_trust_report.json + security/permission_policy.json", + "evidence_link": "../security/permission_policy.md", + "verification_command": "python3 scripts/trust_check.py ." + }, + { + "gate_key": "permission-runtime", + "label": "权限探针", + "status": "warn", + "priority": "warning", + "summary": "生成并修复目标包的 runtime permission probe 报告。", + "why": "目标端即使只能提供 metadata fallback,也必须明确 native enforcement 缺口、表示位置和 operator note。", + "source_fix": "dist/targets/*/adapter.json + reports/runtime_permission_probes.md", + "source_refs": [ + { + "path": "reports/runtime_permission_probes.md", + "label": "runtime permission probes", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "dist/targets/openai/adapter.json", + "label": "OpenAI adapter", + "kind": "package", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "dist/targets/claude/adapter.json", + "label": "Claude adapter", + "kind": "package", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "dist/targets/generic/adapter.json", + "label": "generic adapter", + "kind": "package", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/runtime_permission_probes.json", + "evidence_link": "runtime_permission_probes.md", + "verification_command": "python3 scripts/probe_runtime_permissions.py . --package-dir dist" + }, + { + "gate_key": "skill-atlas", + "label": "组合治理", + "status": "warn", + "priority": "warning", + "summary": "处理 portfolio 里的路由冲突、owner 缺口、stale skill 和重复能力。", + "why": "单个 Skill 质量很高仍可能在团队 skill library 中被相邻 Skill 冲突削弱。", + "source_fix": "reports/skill_atlas.html + skill_atlas/catalog.json", + "source_refs": [ + { + "path": "skill_atlas/catalog.json", + "label": "skill atlas catalog", + "kind": "atlas", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "skill_atlas/policy.json", + "label": "atlas scope policy", + "kind": "policy", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/skill_atlas.html", + "label": "skill atlas report", + "kind": "report", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/skill_atlas.json", + "evidence_link": "skill_atlas.html", + "verification_command": "python3 scripts/build_skill_atlas.py --workspace-root ." + }, + { + "gate_key": "operations-loop", + "label": "运营回路", + "status": "warn", + "priority": "warning", + "summary": "记录 metadata-only 使用事件,或明确当前 release 缺少真实使用信号。", + "why": "没有运营回路时,reviewer 无法判断采用率、误触发、坏输出和 review overdue 的真实影响。", + "source_fix": "reports/adoption_drift_report.md", + "source_refs": [ + { + "path": "reports/adoption_drift_report.md", + "label": "adoption drift report", + "kind": "report", + "line": 1, + "exists": true, + "link": "adoption_drift_report.md" + }, + { + "path": "references/telemetry-drift-method.md", + "label": "telemetry method", + "kind": "method", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/adoption_drift_report.json", + "evidence_link": "adoption_drift_report.md", + "verification_command": "python3 scripts/render_adoption_drift_report.py . --record-event skill_activation --activation-type explicit --outcome accepted" + }, + { + "gate_key": "review-waivers", + "label": "人工批准", + "status": "warn", + "priority": "warning", + "summary": "对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。", + "why": "warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。", + "source_fix": "reports/review_waivers.md", + "source_refs": [ + { + "path": "reports/review_waivers.md", + "label": "waiver ledger", + "kind": "report", + "line": 1, + "exists": true, + "link": "review_waivers.md" + }, + { + "path": "references/review-waiver-method.md", + "label": "waiver method", + "kind": "method", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/review_waivers.json", + "evidence_link": "review_waivers.md", + "verification_command": "python3 scripts/render_review_waivers.py ." + }, + { + "gate_key": "release-notes", + "label": "发布路线", + "status": "warn", + "priority": "warning", + "summary": "确认 promotion、upgrade diff、breaking changes、migration guide 和 known limitations。", + "why": "发布说明不完整会让使用者无法判断升级风险和迁移动作。", + "source_fix": "reports/upgrade_check.md + docs/migration-v2.md", + "source_refs": [ + { + "path": "reports/upgrade_check.md", + "label": "upgrade check", + "kind": "report", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "docs/migration-v2.md", + "label": "migration guide", + "kind": "docs", + "line": null, + "exists": false, + "link": "" + }, + { + "path": "reports/promotion_decisions.md", + "label": "promotion decisions", + "kind": "report", + "line": null, + "exists": false, + "link": "" + } + ], + "evidence": "reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md", + "evidence_link": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/docs/migration-v2.md", + "verification_command": "python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json" + } + ], + "evidence_paths": { + "skill_overview": "reports/skill-overview.html", + "review_viewer": "reports/review-viewer.html", + "output_eval": "reports/output_quality_scorecard.md", + "output_execution": "reports/output_execution_runs.md", + "output_blind_review": "reports/output_blind_review_pack.md", + "output_review_kit": "reports/output_review_kit.md", + "output_review_kit_html": "reports/output_review_kit.html", + "output_review_decisions": "reports/output_review_decisions.json", + "output_review_adjudication": "reports/output_review_adjudication.md", + "benchmark_reproducibility": "reports/benchmark_reproducibility.md", + "skill_os2_coverage": "reports/skill_os2_coverage.md", + "runtime_conformance": "reports/conformance_matrix.md", + "trust_report": "reports/security_trust_report.md", + "python_compatibility": "reports/python_compatibility.md", + "architecture_maintainability": "reports/architecture_maintainability.md", + "permission_policy": "security/permission_policy.md", + "runtime_permissions": "reports/runtime_permission_probes.md", + "skill_atlas": "reports/skill_atlas.html", + "compiled_targets": "reports/compiled_targets.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "world_class_evidence_plan": "reports/world_class_evidence_plan.md", + "world_class_evidence_ledger": "reports/world_class_evidence_ledger.md", + "world_class_evidence_intake": "reports/world_class_evidence_intake.md", + "world_class_submission_review": "reports/world_class_submission_review.md", + "world_class_operator_runbook": "reports/world_class_operator_runbook.md", + "world_class_operator_runbook_html": "reports/world_class_operator_runbook.html", + "world_class_claim_guard": "reports/world_class_claim_guard.md", + "registry_audit": "reports/registry_audit.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "upgrade_check": "reports/upgrade_check.md", + "migration": "docs/migration-v2.md", + "skill_ir": "reports/skill-ir.json" + }, + "data": { + "overview": { + "skill_summary": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "display_name": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "maturity": "production", + "updated_at": "2026-06-15", + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": [ + "SKILL.md", + "agents/interface.yaml", + "reports/skill-ir.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-overview.html" + ], + "flow": [ + "输入材料", + "Skill 包体", + "可复用能力" + ] + }, + "scorecard": { + "completeness_score": { + "label": "完整度", + "score": 92, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 60, + "reasons": [ + "已生成 12 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。", + "证据不足:缺少 output_quality_scorecard.json, conformance_matrix.json, security_trust_report.json。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 85, + "reasons": [ + "SKILL.md 约 864 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 证据不足,重复执行逻辑可能仍靠人工。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 78, + "reasons": [ + "入口约 864 个词/字,references 约 565 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本处于可控区间。" + ] + } + }, + "capability_profile": { + "archetype": "production", + "task_family": "Creative generation", + "maturity": "production", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "principle_model": { + "nodes": [ + { + "title": "意图澄清", + "body": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Ask one focused question and update intent context before adding assets." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "contract_boundary": { + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "用户提供的工作流、提示词、文档、记录或散乱笔记", + "期望沉淀的复用场景、排除项、约束和质量标准" + ], + "outputs": [ + "可路由的 SKILL.md", + "agents/interface.yaml 元数据", + "必要的 references、scripts、evals、reports 证据", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "已有原始资料,但还没有清晰输入、输出和后续迭代路径。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "quality_review": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。", + "已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。", + "已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。" + ], + "gaps": [ + "证据充分需要补强:已生成 12 / 20 类报告证据。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers." + ] + }, + "prompt_quality": { + "overall_quality_score": 85.0, + "highlights": [ + "Primary prompt task family: Creative generation.", + "Complexity: complex — multiple inputs, constraints, or task families require tradeoff handling", + "Completeness: 80/100.", + "Clarity: 90/100." + ] + }, + "system_model": { + "stability": { + "score": 77, + "band": "stable-first-pass" + }, + "highlights": [ + "Stability: stable-first-pass (77/100).", + "Owned job: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "Leverage: Clarify the real job boundary — Ask one focused question and update intent context before adding assets.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language." + ] + } + }, + "risk_governance": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 2, + "signal": "已生成 12 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 2, + "signal": "已生成 12 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 2, + "signal": "SKILL.md 约 864 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "table", + "report", + "brief" + ], + "score": 4, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "screenshot", + "visual", + "screen" + ], + "score": 3, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "citation", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "copy", + "content" + ], + "score": 2, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + }, + { + "key": "tutorial_quality", + "label": "Tutorial quality", + "matched_keywords": [ + "tutorial" + ], + "score": 1, + "risks": [ + "Generic section headings make the tutorial feel templated instead of fitted to the learner's task.", + "Steps may explain what to do without naming the exact check that proves the step worked.", + "Examples can become too abstract when the input material is concrete." + ], + "constraints": [ + "Write headings from the user's domain nouns and desired outcome, not from generic labels like Overview or Key Points.", + "Pair each major step with a visible success check or expected intermediate output.", + "Use one concrete worked example before adding broad principles." + ], + "self_repair": [ + "Replace generic H2/H3 headings with task-specific headings before final output.", + "Scan every numbered step for a missing verification cue." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "package_assets": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 2 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 0 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 2 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 34 + } + ], + "file_count": 42, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 2 + }, + { + "label": "scripts", + "value": 1 + }, + { + "label": "evals", + "value": 2 + }, + { + "label": "reports", + "value": 34 + } + ] + }, + "iteration_roadmap": { + "items": [ + { + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations." + }, + { + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint." + }, + { + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset." + } + ] + }, + "report_contract": { + "schema_version": "2.0", + "html_report": "reports/skill-overview.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "layout": "kami-white-audit-v2", + "nav_labels": [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线" + ], + "nav_labels_en": [ + "Overview", + "Metrics", + "Profile", + "Principle", + "Contract", + "Quality", + "Risk", + "Assets", + "Roadmap" + ] + }, + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "display_name": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "logic_steps": [ + "读取用户输入,识别目标受众、内容渠道、关键词、品牌边界、禁止表达和必须覆盖的信息。", + "对照 `references/brief-structure.md` 生成简报骨架:目标、洞察、角度、关键词、结构、证据、风险和验收标准。", + "如果缺少核心输入,先提出最多三个澄清问题,不用假设补全用户没有提供的事实。", + "用 `evals/trigger_cases.jsonl` 检查当前请求是否属于 GEO 内容简报场景。", + "用 `evals/output_cases.jsonl` 检查输出是否包含输入摘要、内容结构、证据要求、禁区和下一步动作。" + ], + "usage_steps": [ + "当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "Use this skill when the request matches: 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" + ], + "package_map": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 2 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 0 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 2 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 34 + } + ], + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。", + "已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。", + "已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。" + ], + "scenarios": [ + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "已有原始资料,但还没有清晰输入、输出和后续迭代路径。" + ], + "trigger_contract": { + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "io_contract": { + "inputs": [ + "用户提供的工作流、提示词、文档、记录或散乱笔记", + "期望沉淀的复用场景、排除项、约束和质量标准" + ], + "outputs": [ + "可路由的 SKILL.md", + "agents/interface.yaml 元数据", + "必要的 references、scripts、evals、reports 证据", + "结构化 Skill 目录,共 8 类关键资产。" + ] + }, + "principles": [ + { + "title": "意图澄清", + "body": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Ask one focused question and update intent context before adding assets." + } + ], + "roadmap": [ + { + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations." + }, + { + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint." + }, + { + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset." + } + ], + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。" + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": "2.0.0", + "target_count": 5, + "trigger_samples": 1, + "output_eval_cases": 0 + }, + "compiled_targets": { + "ok": true, + "schema_version": "1.0", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter_mode": "metadata-adapter", + "degradation_strategy": "metadata-adapter", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "permission_enforcement": "metadata-only", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "adapter_mode": "neutral-source-plus-adapter", + "degradation_strategy": "neutral-source-plus-adapter", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "permission_enforcement": "metadata-fallback", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [] + }, + { + "target": "generic", + "status": "pass", + "adapter_mode": "agent-skills-compatible", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills compatible neutral package", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "targets/generic/adapter.json" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "agent-skills-compatible", + "status": "pass", + "adapter_mode": "neutral-agent-skills-source", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills standard source tree", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "vscode", + "status": "pass", + "adapter_mode": "vscode-agent-skills-adapter", + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "permission_enforcement": "client-or-workspace-trust", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [] + } + ], + "failures": [], + "warnings": [] + }, + "output_quality": {}, + "output_execution": { + "ok": false, + "summary": {}, + "runner": {}, + "failures": [] + }, + "output_blind_review": { + "summary": {}, + "seed": "", + "pair_count": 0, + "answer_key_separate": false + }, + "output_review_kit": { + "ok": false, + "summary": {}, + "artifacts": {}, + "failures": [] + }, + "output_review_adjudication": { + "ok": false, + "summary": {}, + "reviewer": "", + "reviewed_at": "", + "failures": [] + }, + "benchmark_reproducibility": { + "ok": false, + "summary": {}, + "commit": "", + "missing_artifacts": [], + "limitations": [] + }, + "runtime_conformance": {}, + "runtime_permissions": { + "ok": false, + "summary": {}, + "expected_capabilities": [], + "targets": [], + "failures": [] + }, + "trust_security": {}, + "skill_atlas": {}, + "registry_distribution": { + "ok": false, + "package": {}, + "failures": [], + "warnings": [] + }, + "package_verification": { + "ok": false, + "summary": {}, + "failures": [], + "warnings": [] + }, + "install_simulation": { + "ok": false, + "summary": {}, + "failures": [], + "warnings": [] + }, + "upgrade_check": { + "ok": false, + "summary": {}, + "upgrade_diff": {}, + "release_notes": [], + "failures": [], + "warnings": [] + }, + "adoption_drift": { + "ok": true, + "summary": { + "event_count": 0, + "adoption_sample_count": 0, + "activation_count": 0, + "accepted_count": 0, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "no-data", + "event_types": {}, + "failure_types": {}, + "source_types": {}, + "command_counts": {} + }, + "next_iteration_candidates": [ + { + "signal": "no telemetry", + "recommendation": "Start with a small metadata-only sample before using telemetry for release decisions." + } + ], + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "failures": [] + }, + "review_waivers": { + "ok": true, + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 0, + "waiverable_open_count": 0, + "non_waivable_count": 0 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "known_gate_keys": [ + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ] + }, + "failures": [], + "warnings": [] + }, + "review_annotations": { + "ok": true, + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [] + }, + "world_class_evidence_plan": { + "ok": false, + "summary": {}, + "tasks": [], + "source_audit": {} + }, + "world_class_evidence_ledger": { + "ok": false, + "summary": {}, + "entries": [], + "source_plan": {} + }, + "synthesis_highlights": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers." + ] + }, + "prompt_quality": { + "overall_quality_score": 85.0, + "highlights": [ + "Primary prompt task family: Creative generation.", + "Complexity: complex — multiple inputs, constraints, or task families require tradeoff handling", + "Completeness: 80/100.", + "Clarity: 90/100." + ] + }, + "system_model": { + "stability": { + "score": 77, + "band": "stable-first-pass" + }, + "highlights": [ + "Stability: stable-first-pass (77/100).", + "Owned job: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "Leverage: Clarify the real job boundary — Ask one focused question and update intent context before adding assets.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language." + ] + }, + "metadata": { + "canonical_format": "agent-skills", + "targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "maturity_tier": "production", + "skill_archetype": "production", + "updated_at": "2026-06-15" + } + }, + "intent_confidence": { + "score": 10, + "band": "low", + "gate_passed": false, + "strengths": [], + "gaps": [ + { + "key": "job_specificity", + "label": "Recurring job is still generic", + "reason": "The current job statement sounds more like a packaging goal than a concrete repeated task.", + "severity": "high" + }, + { + "key": "real_inputs", + "label": "Real inputs are missing", + "reason": "Without real inputs, it is hard to choose assets, scripts, or examples.", + "severity": "high" + }, + { + "key": "primary_output", + "label": "Primary output is missing", + "reason": "The package does not yet know what it must hand back.", + "severity": "high" + }, + { + "key": "exclusions", + "label": "Near-neighbor exclusions are missing", + "reason": "The route may blur into nearby requests without an exclusion list.", + "severity": "high" + }, + { + "key": "constraints", + "label": "Constraints are missing", + "reason": "The package does not yet know which tradeoffs matter most.", + "severity": "high" + }, + { + "key": "standards", + "label": "Quality bar is implied, not explicit", + "reason": "The first evaluation target is still underspecified.", + "severity": "medium" + } + ], + "follow_up_questions": [ + { + "slot": "job", + "question": "If you say it plainly, what concrete repeated task should this skill own every time?", + "why": "A skill needs a real recurring job, not only a generic packaging goal.", + "list": false, + "label": "Recurring job is still generic", + "severity": "high" + }, + { + "slot": "real_inputs", + "question": "What material will people actually hand to this skill in practice?", + "why": "Real input shape decides whether references, scripts, or examples are needed.", + "list": true, + "label": "Real inputs are missing", + "severity": "high" + }, + { + "slot": "primary_output", + "question": "What finished hand-back should this skill return so the next person can keep moving?", + "why": "The output is the anchor for package design and review.", + "list": false, + "label": "Primary output is missing", + "severity": "high" + } + ], + "anchor_sentence": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "recommended_action": "Pause before deep authoring and close the highest-leverage gaps first.", + "context": { + "job": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "real_inputs": [], + "primary_output": "", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [] + } + }, + "intent_dialogue": { + "skill_name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "focus": "trigger-and-output", + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", + "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster." + ], + "opening_styles": [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。" + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。" + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。" + } + ], + "optional_scaffold": [ + "The repeated job it should reliably handle", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse" + ], + "questions": [ + { + "question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?", + "why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape." + }, + { + "question": "When someone reaches for this skill in the real world, what materials will they actually hand to it?", + "why": "Input shape decides whether references, scripts, or templates are needed." + }, + { + "question": "What finished output should it hand back so the user can immediately keep moving?", + "why": "Outputs should drive the package structure before extra guidance is added." + }, + { + "question": "Which nearby requests should this skill politely refuse so the boundary stays clean?", + "why": "The exclusion list is the fastest route to better trigger quality." + }, + { + "question": "What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?", + "why": "Constraints decide how much structure, packaging, and review the skill actually needs." + }, + { + "question": "Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example?", + "why": "A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material." + }, + { + "question": "What repeated manual step should become a deterministic asset first?", + "why": "This usually reveals whether a script or reference should be created next." + } + ], + "output": { + "capability_sentence": "Geo Content Brief Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", + "required_capture": [ + "recurring job", + "real inputs", + "required outputs", + "exclusions", + "constraints", + "reference preferences", + "first evaluation target" + ], + "recommended_first_gate": "trigger and boundary" + } + }, + "route_scorecard": {}, + "output_quality": {}, + "output_execution": {}, + "output_blind_review": {}, + "output_review_kit": {}, + "output_review_adjudication": {}, + "benchmark_reproducibility": {}, + "skill_os2_coverage": {}, + "compiled_targets": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-15", + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "schema_version": "1.0", + "target": "openai", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "openai", + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "degradation_strategy": "metadata-adapter", + "target_runtime": { + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "target_transform": { + "target": "openai", + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "metadata-adapter", + "permission_representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation_policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "resource_strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "script_strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "permission_enforcement": "metadata-only" + }, + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "claude", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "claude", + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "degradation_strategy": "neutral-source-plus-adapter", + "target_runtime": { + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Claude-compatible neutral source folder with adapter notes" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "target_transform": { + "target": "claude", + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source-plus-adapter", + "permission_representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation_policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "resource_strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "script_strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "permission_enforcement": "metadata-fallback" + }, + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "generic", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "generic", + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills compatible neutral package" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "target_transform": { + "target": "generic", + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "targets/generic/adapter.json::target_permission_contract", + "native_surface": "Agent Skills compatible neutral package", + "activation_policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "resource_strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "script_strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "agent-skills-compatible", + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills standard source tree" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "target_transform": { + "target": "agent-skills-compatible", + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "adapter metadata", + "native_surface": "Agent Skills standard source tree", + "activation_policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "resource_strategy": "Keep optional directories as relative resources next to SKILL.md.", + "script_strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "vscode", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "reports/skill-ir.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "version": "0.1.0", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ], + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + } + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill", + "counts": { + "trigger": 0, + "output": 0, + "adversarial": 0, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "vscode", + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "target_runtime": { + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "VS Code/Copilot Agent Skills project or user scope" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "missing-security-trust-report", + "source_available": false, + "declared_capabilities": [], + "review_required": false, + "capabilities": { + "network": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "file_write": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "subprocess": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": false, + "checked_count": 0, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 0, + "file_write_script_count": 0, + "subprocess_script_count": 0, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": false, + "declared_capabilities": [], + "capability_counts": { + "network": 0, + "file_write": 0, + "subprocess": 0, + "interactive": 0 + }, + "evidence": "missing-security-trust-report", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 0, + "scripts": 0, + "assets": 0, + "reports": 13 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 0, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [], + "review_required": false + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "target_transform": { + "target": "vscode", + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "permission_representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation_policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "resource_strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "script_strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "permission_enforcement": "client-or-workspace-trust" + }, + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [], + "failures": [] + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "json": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.json", + "markdown": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/compiled_targets.md" + } + }, + "conformance": {}, + "runtime_permissions": {}, + "trust": {}, + "python_compatibility": {}, + "architecture_maintainability": {}, + "context_budget": {}, + "promotion": {}, + "atlas": {}, + "adoption_drift": { + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-15T09:03:27Z", + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "summary": { + "event_count": 0, + "adoption_sample_count": 0, + "activation_count": 0, + "accepted_count": 0, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "no-data", + "event_types": {}, + "failure_types": {}, + "source_types": {}, + "command_counts": {} + }, + "adoption_by_skill": [], + "next_iteration_candidates": [ + { + "signal": "no telemetry", + "recommendation": "Start with a small metadata-only sample before using telemetry for release decisions." + } + ], + "recent_events": [], + "failures": [], + "artifacts": { + "events_jsonl": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/telemetry_events.jsonl", + "json": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.json", + "markdown": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/adoption_drift_report.md" + } + }, + "review_waivers": { + "schema_version": "1.0", + "ok": true, + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "generated_at": "2026-06-15", + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 0, + "waiverable_open_count": 0, + "non_waivable_count": 0 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "known_gate_keys": [ + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ] + }, + "waivers": [], + "waiver_candidates": [], + "failures": [], + "warnings": [], + "artifacts": { + "json": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.json", + "markdown": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.md" + } + }, + "review_annotations": { + "schema_version": "1.0", + "ok": true, + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "source": "reports/review_annotations_input.json", + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [], + "template_written": false, + "artifacts": { + "json": "reports/review_annotations.json", + "markdown": "reports/review_annotations.md" + } + }, + "world_class_evidence_ledger": {}, + "world_class_evidence_intake": {}, + "world_class_submission_review": {}, + "world_class_operator_runbook": {}, + "world_class_claim_guard": {}, + "registry": {}, + "package_verification": {}, + "install_simulation": {}, + "upgrade_check": {}, + "manifest": { + "name": "geo-content-brief-skill", + "version": "0.1.0", + "owner": "Yao Team", + "updated_at": "2026-06-15", + "status": "active", + "maturity_tier": "production", + "lifecycle_stage": "production-preview", + "context_budget_tier": "production", + "review_cadence": "per-release", + "skill_archetype": "production", + "target_platforms": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "factory_components": [ + "references", + "scripts", + "reports" + ] + }, + "frontmatter": { + "name": "geo-content-brief-skill", + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" + }, + "interface": { + "interface": { + "display_name": "Geo Content Brief Skill", + "short_description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。", + "default_prompt": "当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。" + }, + "compatibility": { + "canonical_format": "agent-skills", + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "activation": { + "mode": "manual", + "paths": [ + "SKILL.md", + "references/brief-structure.md", + "evals/trigger_cases.jsonl" + ] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "degradation": { + "openai": "metadata-adapter", + "claude": "neutral-source-plus-adapter", + "generic": "neutral-source", + "vscode": "agent-skills-source-with-vscode-notes" + } + } + } + }, + "artifacts": { + "html": "reports/review-studio.html", + "json": "reports/review-studio.json" + } +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.html b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.html new file mode 100644 index 0000000..1b701dd --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.html @@ -0,0 +1,376 @@ + + + + + + Geo Content Brief Skill Review Viewer + + + +
+
+

Review Viewer

+

Geo Content Brief Skill

+

将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。

+
+ maturity: production + archetype: production + format: agent-skills + updated: 2026-06-15 + intent confidence: 10 / 100 +
+
+ +
+

Architecture at a glance

+
Inputs
workflow, prompt, transcript, docs, or notes
Boundary
将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。
Logic
读取用户输入,识别目标受众、内容渠道、关键词、品牌边界、禁止表达和必须覆盖的信息。; 对照 `references/brief-structure.md` 生成简报骨架:目标、洞察、角度、关键词、结构、证据、风险和验收标准。; 如果缺少核心输入,先提出最多三个澄清问题,不用假设补全用户没有提供的事实。
Usage
当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。; Use this skill when the request matches: 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。
Next
Review the top iteration directions before growing the package.
+
+ +
+
+

Core logic

+
  • 读取用户输入,识别目标受众、内容渠道、关键词、品牌边界、禁止表达和必须覆盖的信息。
  • 对照 `references/brief-structure.md` 生成简报骨架:目标、洞察、角度、关键词、结构、证据、风险和验收标准。
  • 如果缺少核心输入,先提出最多三个澄清问题,不用假设补全用户没有提供的事实。
  • 用 `evals/trigger_cases.jsonl` 检查当前请求是否属于 GEO 内容简报场景。
  • 用 `evals/output_cases.jsonl` 检查输出是否包含输入摘要、内容结构、证据要求、禁区和下一步动作。
+
+
+

How to use it

+
  • 当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。
  • Use this skill when the request matches: 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。
+
+
+ +
+
+

Intent questions

+
  • If this skill worked beautifully, what recurring job would it reliably handle for the user every time?
    This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape.
  • When someone reaches for this skill in the real world, what materials will they actually hand to it?
    Input shape decides whether references, scripts, or templates are needed.
  • What finished output should it hand back so the user can immediately keep moving?
    Outputs should drive the package structure before extra guidance is added.
  • Which nearby requests should this skill politely refuse so the boundary stays clean?
    The exclusion list is the fastest route to better trigger quality.
  • What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?
    Constraints decide how much structure, packaging, and review the skill actually needs.
+
+
+

Why this package is strong

+
  • 触发面保持精简,并锚定在 frontmatter description。
  • 已生成 Skill IR,核心语义可先于平台打包被审查和迁移。
  • 已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。
  • 已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。
  • 已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。
  • 已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。
+
+
+ +
+
+

Borrow plan

+
  • No external benchmark objects recorded yet. Add 2 to 5 references before deepening the package.
+
+
+

Compare view

+

No baseline comparison has been recorded for this package yet.

+
+
+ +
+

Variant diff studio

+

No description optimization compare payload is attached yet.

+
+ +
+
+

Evidence readiness

+

Readiness score: 57/100

+
  • Intent clarity · needs review
    10/100 intent confidence.
  • Benchmark coverage · needs evidence
    0 GitHub benchmark repositories attached.
  • Pattern gate · ready
    3 accepted, 0 deferred using threshold 2/4.
  • Conflict handling · decision needed
    The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.
  • Output risk profile · ready
    5 output risk families attached.
  • Artifact design profile · ready
    High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.
  • Prompt quality profile · ready
    85.0/100 prompt-facing quality score.
+
+
+

Honest boundary check

+
    +
  • Are the known limits visible before the package deepens?
  • +
  • Does the evidence support the borrowed patterns?
  • +
  • Should uncertainty become a clarification question instead of more structure?
  • +
+
+
+ +
+
+

Output risk profile

+
  • Markdown readability
    Tables can render as dense grids with weak hierarchy or poor mobile readability.; Long bullets can make the output look complete while hiding the actual decision logic.
  • Screenshot and visual capture
    Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.; Missing screenshots can cause the skill to invent visual references instead of declaring the gap.
  • Citation and footnote clutter
    Footnote markers or dense citation notes can interrupt the reading flow.; Evidence can be over-attached to obvious statements and under-attached to risky claims.
+
+
+

Self-repair checks

+
  • Preview whether each table still reads well when columns are narrow.
  • Convert any table with paragraph-length cells into bullets or cards.
  • Check that every screenshot reference points to a real provided or generated asset.
  • Reword any visual instruction that depends on an unseen screen state.
  • Remove decorative citations that do not support a material claim.
+
+
+ +
+
+

Artifact design profile

+

Design system: metric editorial

+
  • Report or brief
    High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.
  • Review viewer
    Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix.
  • Dashboard or metrics page
    Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.
+
+
+

Visual quality gates

+
  • Keep the first screen useful without requiring the reader to parse every detail.
  • Use tables only for comparisons; move explanations below the table.
  • Keep source notes readable without flooding the body with markers.
  • Make differences visible instead of hiding them in prose.
  • Separate author-facing recommendations from reviewer-only evidence.
+
+
+ +
+
+

Prompt quality profile

+

Relevance: prompt-heavy · score 85.0 / 100 · complexity complex

+
  • Completeness · 80 / 100
    Name missing inputs, outputs, constraints, or success standards before deepening the package.
  • Clarity · 90 / 100
    Replace broad verbs with observable actions and define what done means.
  • Consistency · 90 / 100
    Check that role, task, format, exclusions, and examples do not contradict each other.
  • Practicality · 95 / 100
    Add runnable steps, examples, or verification cues instead of abstract advice.
  • Specificity · 70 / 100
    Anchor wording in the user's audience, domain nouns, and target outcome.
+
+
+

RTF to skill mapping

+
  • Role
    Use a taste-aware creator role with clear audience, tone, and originality boundaries.
  • Task
    Generate variants, explain selection logic, and preserve the user's distinctive constraints.
  • Format
    Return options with rationale, selection criteria, and refinement paths.
+
+
+ +
+
+

Reference coach

+

No GitHub benchmark scan has been attached to this package yet.

+
+
+

Decide before you deepen

+
    +
  • Choose one pattern to borrow on purpose, not three at once.
  • +
  • State one thing this skill will not inherit from the benchmark objects.
  • +
  • Only deepen the package after that choice is visible in the boundary or execution flow.
  • +
+
+
+ +
+
+

Reference synthesis

+

Official skill anatomy and context discipline

Borrow now

  • Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.

Avoid

  • Do not let packaging or platform concerns swallow the core job boundary.

Hypothesis-test-learn loop

Borrow now

  • Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.

Avoid

  • Do not create experimental overhead that exceeds the skill's real risk tier.

Boundary-first design

Borrow now

  • Borrow the discipline of defining what the skill should not own before growing the package.

Avoid

  • Do not expand execution assets until route boundaries stay clean.
+
+
+

Borrow now

+
  • Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.
  • Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.
  • Borrow the discipline of defining what the skill should not own before growing the package.
+

Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call.

+
+
+ +
+

Top three next moves

+

Tighten trigger and exclusions

The package needs clearer near-neighbor exclusions before it grows.

  • Add 3 to 5 should-trigger and should-not-trigger examples.
  • Refine the frontmatter description to name the recurring job and non-goals.
  • Run a first trigger evaluation pass before expanding the package.
Unlocks: Cleaner routing and fewer accidental activations.

Add the first execution asset

The package is still mostly prose. Add one asset that removes repeated manual work.

  • Move stable procedural guidance into references if users will need it repeatedly.
  • Create one deterministic helper script if a repeated step can be executed instead of described.
  • Keep the main SKILL.md compact and route-oriented.
Unlocks: Stronger execution quality without bloating the entrypoint.

Promote from scaffold to production-ready

The first version exists; the next gain usually comes from adding the smallest useful gates.

  • Decide whether this skill is personal, team-reused, or library-grade.
  • Add only the gates that match that risk level.
  • Record lifecycle metadata and review cadence once reuse becomes real.
Unlocks: A clearer path from exploratory package to maintained asset.
+
+ +
+
+

Recent feedback

+
  • No lightweight feedback captured yet. Use `yao.py feedback` to record quick review notes.
+
+
+

Promotion status

+

No promotion summary is attached to this package yet.

+
+
+ +
+
+

Package map

+
  • SKILL.md — Skill entrypoint
  • README.md — Human-readable usage guide
  • agents/interface.yaml — Neutral interface metadata
  • manifest.json — Lifecycle and portability metadata
  • references — Extended guidance and reusable notes
  • scripts — Deterministic helpers or local tooling
  • evals — Trigger and quality checks
  • reports — Generated evidence and overview artifacts
+
+
+

First-pass review frame

+
    +
  • Does the trigger stay narrow enough for the intended job?
  • +
  • Does the archetype match the real reuse level?
  • +
  • Are we adding structure faster than we are adding reliability?
  • +
  • Should the next step be trigger tightening, execution assets, or portability hardening?
  • +
+
+
+ +
+
+

Authoring discipline

+
    +
  • Name unresolved assumptions before deepening the package.
  • +
  • Keep the package no larger than the recurring job requires.
  • +
  • Touch only files that directly support the requested change.
  • +
  • Tie every meaningful new artifact to a check or reviewer note.
  • +
+
+
+

Reviewer guardrails

+
    +
  • Block speculative features that are not backed by real workflow variation.
  • +
  • Move unverifiable ideas into next-step candidates instead of baseline structure.
  • +
  • Reject decorative folders, reports, or governance that do not reduce risk.
  • +
  • Ask for one high-leverage clarification when job, output, or exclusion is still fuzzy.
  • +
+
+
+
+ + diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.json new file mode 100644 index 0000000..d956d9a --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review-viewer.json @@ -0,0 +1,1921 @@ +{ + "overview": { + "skill_summary": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "display_name": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "maturity": "production", + "updated_at": "2026-06-15", + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": [ + "SKILL.md", + "agents/interface.yaml", + "reports/skill-ir.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-overview.html" + ], + "flow": [ + "输入材料", + "Skill 包体", + "可复用能力" + ] + }, + "scorecard": { + "completeness_score": { + "label": "完整度", + "score": 92, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 60, + "reasons": [ + "已生成 12 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。", + "证据不足:缺少 output_quality_scorecard.json, conformance_matrix.json, security_trust_report.json。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 85, + "reasons": [ + "SKILL.md 约 864 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 证据不足,重复执行逻辑可能仍靠人工。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 78, + "reasons": [ + "入口约 864 个词/字,references 约 565 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本处于可控区间。" + ] + } + }, + "capability_profile": { + "archetype": "production", + "task_family": "Creative generation", + "maturity": "production", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "principle_model": { + "nodes": [ + { + "title": "意图澄清", + "body": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Ask one focused question and update intent context before adding assets." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "contract_boundary": { + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "用户提供的工作流、提示词、文档、记录或散乱笔记", + "期望沉淀的复用场景、排除项、约束和质量标准" + ], + "outputs": [ + "可路由的 SKILL.md", + "agents/interface.yaml 元数据", + "必要的 references、scripts、evals、reports 证据", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "已有原始资料,但还没有清晰输入、输出和后续迭代路径。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "quality_review": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。", + "已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。", + "已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。" + ], + "gaps": [ + "证据充分需要补强:已生成 12 / 20 类报告证据。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers." + ] + }, + "prompt_quality": { + "overall_quality_score": 85.0, + "highlights": [ + "Primary prompt task family: Creative generation.", + "Complexity: complex — multiple inputs, constraints, or task families require tradeoff handling", + "Completeness: 80/100.", + "Clarity: 90/100." + ] + }, + "system_model": { + "stability": { + "score": 77, + "band": "stable-first-pass" + }, + "highlights": [ + "Stability: stable-first-pass (77/100).", + "Owned job: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "Leverage: Clarify the real job boundary — Ask one focused question and update intent context before adding assets.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language." + ] + } + }, + "risk_governance": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 2, + "signal": "已生成 12 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 2, + "signal": "已生成 12 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 2, + "signal": "SKILL.md 约 864 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "table", + "report", + "brief" + ], + "score": 4, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "screenshot", + "visual", + "screen" + ], + "score": 3, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "citation", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "copy", + "content" + ], + "score": 2, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + }, + { + "key": "tutorial_quality", + "label": "Tutorial quality", + "matched_keywords": [ + "tutorial" + ], + "score": 1, + "risks": [ + "Generic section headings make the tutorial feel templated instead of fitted to the learner's task.", + "Steps may explain what to do without naming the exact check that proves the step worked.", + "Examples can become too abstract when the input material is concrete." + ], + "constraints": [ + "Write headings from the user's domain nouns and desired outcome, not from generic labels like Overview or Key Points.", + "Pair each major step with a visible success check or expected intermediate output.", + "Use one concrete worked example before adding broad principles." + ], + "self_repair": [ + "Replace generic H2/H3 headings with task-specific headings before final output.", + "Scan every numbered step for a missing verification cue." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "package_assets": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 2 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 0 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 2 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 34 + } + ], + "file_count": 42, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 2 + }, + { + "label": "scripts", + "value": 1 + }, + { + "label": "evals", + "value": 2 + }, + { + "label": "reports", + "value": 34 + } + ] + }, + "iteration_roadmap": { + "items": [ + { + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations." + }, + { + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint." + }, + { + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset." + } + ] + }, + "report_contract": { + "schema_version": "2.0", + "html_report": "reports/skill-overview.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "layout": "kami-white-audit-v2", + "nav_labels": [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线" + ], + "nav_labels_en": [ + "Overview", + "Metrics", + "Profile", + "Principle", + "Contract", + "Quality", + "Risk", + "Assets", + "Roadmap" + ] + }, + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "display_name": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "logic_steps": [ + "读取用户输入,识别目标受众、内容渠道、关键词、品牌边界、禁止表达和必须覆盖的信息。", + "对照 `references/brief-structure.md` 生成简报骨架:目标、洞察、角度、关键词、结构、证据、风险和验收标准。", + "如果缺少核心输入,先提出最多三个澄清问题,不用假设补全用户没有提供的事实。", + "用 `evals/trigger_cases.jsonl` 检查当前请求是否属于 GEO 内容简报场景。", + "用 `evals/output_cases.jsonl` 检查输出是否包含输入摘要、内容结构、证据要求、禁区和下一步动作。" + ], + "usage_steps": [ + "当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "Use this skill when the request matches: 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" + ], + "package_map": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 2 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 0 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 2 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 34 + } + ], + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。", + "已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。", + "已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。" + ], + "scenarios": [ + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "已有原始资料,但还没有清晰输入、输出和后续迭代路径。" + ], + "trigger_contract": { + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "io_contract": { + "inputs": [ + "用户提供的工作流、提示词、文档、记录或散乱笔记", + "期望沉淀的复用场景、排除项、约束和质量标准" + ], + "outputs": [ + "可路由的 SKILL.md", + "agents/interface.yaml 元数据", + "必要的 references、scripts、evals、reports 证据", + "结构化 Skill 目录,共 8 类关键资产。" + ] + }, + "principles": [ + { + "title": "意图澄清", + "body": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Ask one focused question and update intent context before adding assets." + } + ], + "roadmap": [ + { + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations." + }, + { + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint." + }, + { + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset." + } + ], + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。" + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": "2.0.0", + "target_count": 5, + "trigger_samples": 1, + "output_eval_cases": 0 + }, + "compiled_targets": { + "ok": true, + "schema_version": "1.0", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter_mode": "metadata-adapter", + "degradation_strategy": "metadata-adapter", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "permission_enforcement": "metadata-only", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "adapter_mode": "neutral-source-plus-adapter", + "degradation_strategy": "neutral-source-plus-adapter", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "permission_enforcement": "metadata-fallback", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [] + }, + { + "target": "generic", + "status": "pass", + "adapter_mode": "agent-skills-compatible", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills compatible neutral package", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "targets/generic/adapter.json" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "agent-skills-compatible", + "status": "pass", + "adapter_mode": "neutral-agent-skills-source", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills standard source tree", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "vscode", + "status": "pass", + "adapter_mode": "vscode-agent-skills-adapter", + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "permission_enforcement": "client-or-workspace-trust", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [] + } + ], + "failures": [], + "warnings": [] + }, + "output_quality": {}, + "output_execution": { + "ok": false, + "summary": {}, + "runner": {}, + "failures": [] + }, + "output_blind_review": { + "summary": {}, + "seed": "", + "pair_count": 0, + "answer_key_separate": false + }, + "output_review_kit": { + "ok": false, + "summary": {}, + "artifacts": {}, + "failures": [] + }, + "output_review_adjudication": { + "ok": false, + "summary": {}, + "reviewer": "", + "reviewed_at": "", + "failures": [] + }, + "benchmark_reproducibility": { + "ok": false, + "summary": {}, + "commit": "", + "missing_artifacts": [], + "limitations": [] + }, + "runtime_conformance": {}, + "runtime_permissions": { + "ok": false, + "summary": {}, + "expected_capabilities": [], + "targets": [], + "failures": [] + }, + "trust_security": {}, + "skill_atlas": {}, + "registry_distribution": { + "ok": false, + "package": {}, + "failures": [], + "warnings": [] + }, + "package_verification": { + "ok": false, + "summary": {}, + "failures": [], + "warnings": [] + }, + "install_simulation": { + "ok": false, + "summary": {}, + "failures": [], + "warnings": [] + }, + "upgrade_check": { + "ok": false, + "summary": {}, + "upgrade_diff": {}, + "release_notes": [], + "failures": [], + "warnings": [] + }, + "adoption_drift": { + "ok": true, + "summary": { + "event_count": 0, + "adoption_sample_count": 0, + "activation_count": 0, + "accepted_count": 0, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "no-data", + "event_types": {}, + "failure_types": {}, + "source_types": {}, + "command_counts": {} + }, + "next_iteration_candidates": [ + { + "signal": "no telemetry", + "recommendation": "Start with a small metadata-only sample before using telemetry for release decisions." + } + ], + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "failures": [] + }, + "review_waivers": { + "ok": true, + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 0, + "waiverable_open_count": 0, + "non_waivable_count": 0 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "known_gate_keys": [ + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ] + }, + "failures": [], + "warnings": [] + }, + "review_annotations": { + "ok": true, + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [] + }, + "world_class_evidence_plan": { + "ok": false, + "summary": {}, + "tasks": [], + "source_audit": {} + }, + "world_class_evidence_ledger": { + "ok": false, + "summary": {}, + "entries": [], + "source_plan": {} + }, + "synthesis_highlights": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers." + ] + }, + "prompt_quality": { + "overall_quality_score": 85.0, + "highlights": [ + "Primary prompt task family: Creative generation.", + "Complexity: complex — multiple inputs, constraints, or task families require tradeoff handling", + "Completeness: 80/100.", + "Clarity: 90/100." + ] + }, + "system_model": { + "stability": { + "score": 77, + "band": "stable-first-pass" + }, + "highlights": [ + "Stability: stable-first-pass (77/100).", + "Owned job: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "Leverage: Clarify the real job boundary — Ask one focused question and update intent context before adding assets.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language." + ] + }, + "metadata": { + "canonical_format": "agent-skills", + "targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "maturity_tier": "production", + "skill_archetype": "production", + "updated_at": "2026-06-15" + } + }, + "intent_confidence": { + "score": 10, + "band": "low", + "gate_passed": false, + "strengths": [], + "gaps": [ + { + "key": "job_specificity", + "label": "Recurring job is still generic", + "reason": "The current job statement sounds more like a packaging goal than a concrete repeated task.", + "severity": "high" + }, + { + "key": "real_inputs", + "label": "Real inputs are missing", + "reason": "Without real inputs, it is hard to choose assets, scripts, or examples.", + "severity": "high" + }, + { + "key": "primary_output", + "label": "Primary output is missing", + "reason": "The package does not yet know what it must hand back.", + "severity": "high" + }, + { + "key": "exclusions", + "label": "Near-neighbor exclusions are missing", + "reason": "The route may blur into nearby requests without an exclusion list.", + "severity": "high" + }, + { + "key": "constraints", + "label": "Constraints are missing", + "reason": "The package does not yet know which tradeoffs matter most.", + "severity": "high" + }, + { + "key": "standards", + "label": "Quality bar is implied, not explicit", + "reason": "The first evaluation target is still underspecified.", + "severity": "medium" + } + ], + "follow_up_questions": [ + { + "slot": "job", + "question": "If you say it plainly, what concrete repeated task should this skill own every time?", + "why": "A skill needs a real recurring job, not only a generic packaging goal.", + "list": false, + "label": "Recurring job is still generic", + "severity": "high" + }, + { + "slot": "real_inputs", + "question": "What material will people actually hand to this skill in practice?", + "why": "Real input shape decides whether references, scripts, or examples are needed.", + "list": true, + "label": "Real inputs are missing", + "severity": "high" + }, + { + "slot": "primary_output", + "question": "What finished hand-back should this skill return so the next person can keep moving?", + "why": "The output is the anchor for package design and review.", + "list": false, + "label": "Primary output is missing", + "severity": "high" + } + ], + "anchor_sentence": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "recommended_action": "Pause before deep authoring and close the highest-leverage gaps first.", + "context": { + "job": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "real_inputs": [], + "primary_output": "", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [] + } + }, + "intent": { + "skill_name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "focus": "trigger-and-output", + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", + "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster." + ], + "opening_styles": [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。" + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。" + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。" + } + ], + "optional_scaffold": [ + "The repeated job it should reliably handle", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse" + ], + "questions": [ + { + "question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?", + "why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape." + }, + { + "question": "When someone reaches for this skill in the real world, what materials will they actually hand to it?", + "why": "Input shape decides whether references, scripts, or templates are needed." + }, + { + "question": "What finished output should it hand back so the user can immediately keep moving?", + "why": "Outputs should drive the package structure before extra guidance is added." + }, + { + "question": "Which nearby requests should this skill politely refuse so the boundary stays clean?", + "why": "The exclusion list is the fastest route to better trigger quality." + }, + { + "question": "What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?", + "why": "Constraints decide how much structure, packaging, and review the skill actually needs." + }, + { + "question": "Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example?", + "why": "A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material." + }, + { + "question": "What repeated manual step should become a deterministic asset first?", + "why": "This usually reveals whether a script or reference should be created next." + } + ], + "output": { + "capability_sentence": "Geo Content Brief Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", + "required_capture": [ + "recurring job", + "real inputs", + "required outputs", + "exclusions", + "constraints", + "reference preferences", + "first evaluation target" + ], + "recommended_first_gate": "trigger and boundary" + } + }, + "reference": { + "skill_name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "scan_focus": [ + { + "label": "Execution pattern", + "reason": "There is deterministic logic in scripts, so compare how strong references separate prose from executable steps." + }, + { + "label": "Portability pattern", + "reason": "The package carries neutral metadata, so scan how good references preserve semantics across targets without forking source." + }, + { + "label": "Method pattern", + "reason": "Use the core job description as the anchor for comparison: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + } + ], + "external_references": [], + "user_references": [], + "local_constraints": [], + "borrow_plan": [ + "External benchmark first: let high-quality public references define the upper bound for method, structure, execution, or portability.", + "User references second: use them to understand taste, standards, and directional preferences without copying source phrasing.", + "Local fit third: use local assets only to detect naming conflicts, private dependencies, or compatibility constraints.", + "Borrow patterns, not prose: extract loops, boundaries, metadata, and operator flow without copying source-specific language.", + "Keep the package light: reject any borrowed pattern that increases context cost faster than it increases reliability." + ], + "priority_rule": "External benchmark objects set the pattern ceiling. User references refine taste and standards. Local files only calibrate fit, risk, and compatibility.", + "non_goals": [ + "Do not copy source prose or branding into the new skill.", + "Do not import gates that cost more context than they save.", + "Do not use benchmark scanning to justify scope creep.", + "Do not let local historical habits outrank stronger public benchmarks." + ] + }, + "iteration": { + "summary": { + "skill_name": "geo-content-brief-skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "maturity_tier": "scaffold", + "selection_rule": "Pick the three smallest next steps that increase reliability more than they increase context cost.", + "recommended_now": "Tighten trigger and exclusions", + "recommended_now_why": "The package needs clearer near-neighbor exclusions before it grows.", + "defer_for_now": "Promote from scaffold to production-ready" + }, + "directions": [ + { + "priority": 1, + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations.", + "do_now": "Do this first.", + "wait_on": "Wait to add broader structure until this move clearly improves reliability." + }, + { + "priority": 2, + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + }, + { + "priority": 3, + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + } + ] + }, + "feedback": {}, + "baseline": {}, + "compare": {}, + "promotion": {}, + "benchmark": {}, + "reference_synthesis": { + "skill_name": "geo-content-brief-skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "intent_confidence": { + "score": 10, + "band": "low", + "gate_passed": false + }, + "github_benchmarks": [], + "source_tracks": [ + { + "source_type": "official", + "name": "Official skill anatomy and context discipline", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "why_relevant": "This track matches: general fit." + }, + { + "source_type": "research", + "name": "Hypothesis-test-learn loop", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "why_relevant": "This track matches: general fit." + }, + { + "source_type": "principles", + "name": "Boundary-first design", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "why_relevant": "This track matches: general fit." + } + ], + "synthesis": { + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "avoid_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier.", + "Do not expand execution assets until route boundaries stay clean." + ], + "quality_risers": [ + "Use GitHub repositories for concrete package and workflow patterns.", + "Use curated official or commercial tracks for entrypoint and operator ergonomics.", + "Use research tracks to justify the smallest evaluation loop that still catches regressions.", + "Use principle tracks to keep the package small, boundary-aware, and outcome-driven." + ], + "pattern_gate": { + "threshold": 2, + "source_count": 3, + "accepted": [ + { + "name": "Official skill anatomy and context discipline", + "source_type": "official", + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": false, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "boundary" + ], + "missing": [ + "distinctiveness" + ], + "score": 3 + }, + { + "name": "Hypothesis-test-learn loop", + "source_type": "research", + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": false, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "boundary" + ], + "missing": [ + "distinctiveness" + ], + "score": 3 + }, + { + "name": "Boundary-first design", + "source_type": "principles", + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": false, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "boundary" + ], + "missing": [ + "distinctiveness" + ], + "score": 3 + } + ], + "deferred": [], + "summary": "3 accepted, 0 deferred using threshold 2/4." + }, + "conflicts": [ + { + "key": "lightweight_vs_governance", + "summary": "The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_preference": "lightweight or speed-first", + "benchmark_pressure": "governance or evaluation-heavy patterns" + } + ], + "recommendation": { + "summary": "Start by borrowing this pattern: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. Avoid this for the first pass: Do not let packaging or platform concerns swallow the core job boundary.", + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed." + ], + "avoid_for_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier." + ], + "why": "There is a real design conflict to resolve: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_decision_required": true + }, + "visibility": { + "mode": "explicit", + "user_decision_required": true, + "reasons": [ + "intent_uncertain", + "design_conflict" + ], + "user_note": "Surface the recommendation because intent is still settling or there is a real design conflict that needs a user call.", + "reviewer_note": "Keep the full benchmark and synthesis evidence visible for authors and reviewers." + }, + "decision_prompt": "Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call.", + "source_mix": { + "github_benchmarks": 0, + "curated_tracks": 3, + "user_references": 0 + } + } + }, + "output_risk": { + "skill_name": "geo-content-brief-skill", + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "table", + "report", + "brief" + ], + "score": 4, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "screenshot", + "visual", + "screen" + ], + "score": 3, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "citation", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "copy", + "content" + ], + "score": 2, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + }, + { + "key": "tutorial_quality", + "label": "Tutorial quality", + "matched_keywords": [ + "tutorial" + ], + "score": 1, + "risks": [ + "Generic section headings make the tutorial feel templated instead of fitted to the learner's task.", + "Steps may explain what to do without naming the exact check that proves the step worked.", + "Examples can become too abstract when the input material is concrete." + ], + "constraints": [ + "Write headings from the user's domain nouns and desired outcome, not from generic labels like Overview or Key Points.", + "Pair each major step with a visible success check or expected intermediate output.", + "Use one concrete worked example before adding broad principles." + ], + "self_repair": [ + "Replace generic H2/H3 headings with task-specific headings before final output.", + "Scan every numbered step for a missing verification cue." + ] + } + ], + "top_risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims." + ], + "output_constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability." + ], + "self_repair_checks": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards.", + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state.", + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ], + "reviewer_note": "Use this report before deepening the package and again before approving example outputs." + }, + "artifact_design": { + "skill_name": "geo-content-brief-skill", + "design_system": "metric editorial", + "primary_artifact": { + "key": "report", + "label": "Report or brief", + "direction": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "matched_keywords": [ + "report", + "brief", + "简报" + ] + }, + "artifact_families": [ + { + "key": "report", + "label": "Report or brief", + "score": 3, + "matched_keywords": [ + "report", + "brief", + "简报" + ], + "direction": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail." + }, + { + "key": "review_viewer", + "label": "Review viewer", + "score": 2, + "matched_keywords": [ + "review", + "审查" + ], + "direction": "Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix." + }, + { + "key": "dashboard", + "label": "Dashboard or metrics page", + "score": 2, + "matched_keywords": [ + "dashboard", + "table" + ], + "direction": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation." + }, + { + "key": "tutorial", + "label": "Tutorial or guide", + "score": 1, + "matched_keywords": [ + "tutorial" + ], + "direction": "Progressive instructional layout with domain-specific section names, short success checks, and examples close to the user's real input." + }, + { + "key": "visual_capture", + "label": "Screenshot or visual evidence", + "score": 1, + "matched_keywords": [ + "screenshot" + ], + "direction": "Evidence-led visual artifact that records source, viewport, crop intent, and the exact region the reader should inspect." + } + ], + "layout_patterns": [ + "thesis", + "evidence blocks", + "decision table", + "risks", + "next actions", + "summary", + "variant comparison", + "evidence" + ], + "design_tokens": { + "type": [ + "Use a distinctive display face or serif for major claims when the artifact is editorial.", + "Use a restrained sans for dense body text and technical details.", + "Use mono only for metadata, paths, commands, labels, and evidence tags." + ], + "color": [ + "Choose colors from the artifact's domain, brand, or evidence mood.", + "Do not default to Kami parchment, purple gradients, or generic SaaS blue unless the content justifies it.", + "Keep accent color limited to decisions, active states, risk, or section anchors." + ], + "spacing": [ + "Prefer clear grid rhythm over floating decorative cards.", + "Increase whitespace around decisions and shrink whitespace around supporting metadata.", + "Split dense content instead of shrinking type or adding scroll traps." + ], + "components": [ + "Use cards for grouped evidence, tables for comparisons, callouts for decisions, and timelines for sequence.", + "Avoid cards inside cards.", + "Keep reviewer-only detail visible but visually quieter than user-facing guidance." + ] + }, + "quality_gates": [ + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers.", + "Make differences visible instead of hiding them in prose.", + "Separate author-facing recommendations from reviewer-only evidence.", + "Surface conflicts clearly and keep routine benchmark synthesis quiet.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each." + ], + "anti_patterns": [ + "Do not copy Kami's fixed parchment background as a default.", + "Do not use generic purple gradients, glass cards, or stock SaaS hero sections unless the content calls for them.", + "Do not let Markdown tables become the default shape for every comparison or explanation.", + "Do not turn reviewer evidence into user-facing clutter.", + "Do not invent screenshots, citations, charts, or UI states." + ], + "reviewer_note": "Use this profile to judge whether the generated artifacts feel designed for their job, not merely rendered." + }, + "prompt_quality": { + "skill_name": "geo-content-brief-skill", + "relevance": "prompt-heavy", + "primary_task_family": { + "key": "creative_generation", + "label": "Creative generation", + "matched_keywords": [ + "copy", + "content", + "内容" + ] + }, + "task_families": [ + { + "key": "creative_generation", + "label": "Creative generation", + "score": 3, + "matched_keywords": [ + "copy", + "content", + "内容" + ], + "role_guidance": "Use a taste-aware creator role with clear audience, tone, and originality boundaries.", + "task_guidance": "Generate variants, explain selection logic, and preserve the user's distinctive constraints.", + "format_guidance": "Return options with rationale, selection criteria, and refinement paths." + }, + { + "key": "execution_operation", + "label": "Execution operation", + "score": 3, + "matched_keywords": [ + "workflow", + "execute", + "执行" + ], + "role_guidance": "Use an operator role with explicit boundaries, inputs, outputs, and failure handling.", + "task_guidance": "Convert the job into ordered steps with validation checks and stop conditions.", + "format_guidance": "Return a runbook-like handoff with commands, checks, owners, and next actions when relevant." + }, + { + "key": "prompt_engineering", + "label": "Prompt engineering", + "score": 3, + "matched_keywords": [ + "prompt", + "role", + "format" + ], + "role_guidance": "Use a prompt engineer role only when role design materially improves execution.", + "task_guidance": "Map Role, Task, and Format into skill behavior rather than copying a large prompt template.", + "format_guidance": "Return a compact prompt contract plus tests, quality matrix, and usage notes." + }, + { + "key": "dialogue_interaction", + "label": "Dialogue interaction", + "score": 2, + "matched_keywords": [ + "dialogue", + "访谈" + ], + "role_guidance": "Use a conversational role that asks only high-leverage questions and remembers the user's goal.", + "task_guidance": "Clarify intent, resolve uncertainty, and converge toward a recommendation instead of a long option list.", + "format_guidance": "Return concise prompts, decision points, and reviewer-visible assumptions." + } + ], + "complexity": { + "band": "complex", + "score": 6, + "reason": "multiple inputs, constraints, or task families require tradeoff handling" + }, + "need_model": { + "explicit_need": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "implicit_need": "The reusable skill needs a stable role, task, and output contract rather than a one-off prompt.", + "scenario": "not yet explicit", + "user_level": "infer from examples and standards; ask only if it changes output depth", + "success_standard": "usable output with clear validation cues" + }, + "rtf_to_skill": { + "role": "Use a taste-aware creator role with clear audience, tone, and originality boundaries.", + "task": "Generate variants, explain selection logic, and preserve the user's distinctive constraints.", + "format": "Return options with rationale, selection criteria, and refinement paths." + }, + "quality_matrix": [ + { + "key": "completeness", + "label": "Completeness", + "score": 80, + "matched_signals": [ + "input", + "output", + "constraint", + "example", + "约束" + ], + "repair": "Name missing inputs, outputs, constraints, or success standards before deepening the package." + }, + { + "key": "clarity", + "label": "Clarity", + "score": 90, + "matched_signals": [ + "clear", + "specific" + ], + "repair": "Replace broad verbs with observable actions and define what done means." + }, + { + "key": "consistency", + "label": "Consistency", + "score": 90, + "matched_signals": [ + "boundary", + "边界" + ], + "repair": "Check that role, task, format, exclusions, and examples do not contradict each other." + }, + { + "key": "practicality", + "label": "Practicality", + "score": 95, + "matched_signals": [ + "execute", + "use", + "workflow", + "执行" + ], + "repair": "Add runnable steps, examples, or verification cues instead of abstract advice." + }, + { + "key": "specificity", + "label": "Specificity", + "score": 70, + "matched_signals": [], + "repair": "Anchor wording in the user's audience, domain nouns, and target outcome." + } + ], + "overall_quality_score": 85.0, + "self_repair_checks": [ + "Check explicit need, implicit need, scenario, user level, and success standard before deepening.", + "Map Role, Task, and Format into skill behavior, not decorative prompt labels.", + "Ask one focused clarification only when missing information changes the package boundary.", + "Add tests or examples for prompt-heavy behavior before treating it as reusable.", + "Keep prompt methodology in references and reports instead of bloating SKILL.md." + ], + "reviewer_note": "Use this profile when the package depends on prompt behavior, role design, output contracts, or conversation quality." + }, + "evidence_readiness": { + "score": 57, + "checks": [ + { + "label": "Intent clarity", + "status": "needs review", + "detail": "10/100 intent confidence." + }, + { + "label": "Benchmark coverage", + "status": "needs evidence", + "detail": "0 GitHub benchmark repositories attached." + }, + { + "label": "Pattern gate", + "status": "ready", + "detail": "3 accepted, 0 deferred using threshold 2/4." + }, + { + "label": "Conflict handling", + "status": "decision needed", + "detail": "The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure." + }, + { + "label": "Output risk profile", + "status": "ready", + "detail": "5 output risk families attached." + }, + { + "label": "Artifact design profile", + "status": "ready", + "detail": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail." + }, + { + "label": "Prompt quality profile", + "status": "ready", + "detail": "85.0/100 prompt-facing quality score." + } + ], + "reviewer_note": "Use this section to decide whether the package is ready to deepen or should stay in discovery." + } +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.json new file mode 100644 index 0000000..36bd147 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.json @@ -0,0 +1,24 @@ +{ + "schema_version": "1.0", + "ok": true, + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "source": "reports/review_annotations_input.json", + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [], + "template_written": false, + "artifacts": { + "json": "reports/review_annotations.json", + "markdown": "reports/review_annotations.md" + } +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.md new file mode 100644 index 0000000..68230eb --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_annotations.md @@ -0,0 +1,19 @@ +# Review Annotations + +This report renders reviewer annotations attached to Review Studio gates and source/report paths. + +- Annotations: `0` +- Open: `0` +- Resolved: `0` +- Deferred: `0` +- Open blockers: `0` +- Open warnings: `0` + +No reviewer annotations recorded yet. + + +## Review Rule + +- Use annotations for reviewer comments tied to a gate or source line. +- Use waivers only for explicit acceptance of warning-level release risk. +- Open blocker annotations should block a release decision until resolved or deferred with rationale. diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.json new file mode 100644 index 0000000..bce91bb --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.json @@ -0,0 +1,44 @@ +{ + "schema_version": "1.0", + "ok": true, + "skill_dir": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill", + "generated_at": "2026-06-15", + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 0, + "waiverable_open_count": 0, + "non_waivable_count": 0 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "known_gate_keys": [ + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ] + }, + "waivers": [], + "waiver_candidates": [], + "failures": [], + "warnings": [], + "artifacts": { + "json": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.json", + "markdown": ".previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.md" + } +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.md new file mode 100644 index 0000000..0e34c96 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/review_waivers.md @@ -0,0 +1,34 @@ +# Review Waivers + +- OK: `True` +- Waivers: `0` +- Active: `0` +- Expired: `0` +- Invalid: `0` +- Covered gates: `none` +- Waiver candidates: `0` +- Open waiverable candidates: `0` +- Non-waivable boundaries: `0` + +## Policy + +- Blocker waivers allowed: `False` +- Minimum reason chars: `20` +- Expiry is required for every waiver. +- World-class evidence completion cannot be waived; it can only be proven by accepted ledger evidence. + +## Waivers + +- None + +## Candidate Actions + +- None + +## Failures + +- None + +## Warnings + +- None diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-ir.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-ir.json new file mode 100644 index 0000000..771b377 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-ir.json @@ -0,0 +1,93 @@ +{ + "schema_version": "2.0.0", + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "job_to_be_done": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "trigger_surface": { + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "should_trigger": [ + "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + ], + "should_not_trigger": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "edge_cases": [] + }, + "workflow": { + "steps": [ + "Understand the request.", + "Execute the main task.", + "Validate the result." + ], + "decision_points": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [], + "scripts": [], + "assets": [], + "reports": [ + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md" + ] + }, + "eval_plan": { + "trigger": [], + "output": [], + "adversarial": [], + "baseline": "without_skill" + }, + "risk": { + "output_risk": "high", + "execution_risk": "low", + "trust_boundary": "personal" + }, + "governance": { + "owner": "Yao Team", + "maturity": "scaffold", + "review_cadence": "per-release", + "review_due": "" + }, + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "source_files": [ + "SKILL.md", + "manifest.json", + "agents/interface.yaml", + "reports/intent-context.json", + "reports/output-risk-profile.json", + "reports/system-model.json" + ] +} diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.html b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.html new file mode 100644 index 0000000..45f5c88 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.html @@ -0,0 +1,986 @@ + + + + + + geo-content-brief-skill Skill 生成审计报告 + + + + +
+ + +
+
+
+
+
+

YAO Skill 生成审计报告YAO Skill Generation Audit

+

geo-content-brief-skill

+

技能审计报告Geo Content Brief Skill Audit Report

+

这份报告默认使用中文简体,把新 Skill 的定位、指标、原理、契约、质量、风险、资产和迭代路线整理为一份可审计的 HTML 报告。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.

+
技能名称:geo-content-brief-skillSkill name: geo-content-brief-skill成熟度:生产Maturity: production格式:Agent SkillsFormat: agent-skills更新时间:2026-06-15Updated: 2026-06-15
+
openaiopenaiclaudeclaudegenericgenericvscodevscode
+
+ +
+
完整度Completeness92SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
触发清晰Trigger clarity100frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
证据充分Evidence depth60已生成 12 / 20 类报告证据。Generated 12 / 20 evidence report types.
上下文成本Context cost78入口约 864 个词/字,references 约 565 个词/字。Entrypoint is about 864 words/characters; references are about 565.
+
+ +
+
+
+

技能概述Overview

+

先用一屏说明这个 Skill 是什么、给谁用、交付什么。A first-screen explanation of what this skill is, who it serves, and what it delivers.

+
+
+
+

作用定位Role

+
  • 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
  • 把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。Turn one-off experience into a reusable, evaluable, and portable skill package.
  • 交付结果:SKILL.md, agents/interface.yaml, reports/skill-ir.json, reports/compiled_targets.md, reports/output_quality_scorecard.md, reports/conformance_matrix.md, reports/security_trust_report.md, reports/skill_atlas.html, reports/registry_audit.md, reports/package_verification.md, reports/install_simulation.md, reports/upgrade_check.md, reports/adoption_drift_report.md, reports/review_waivers.md, reports/review_annotations.md, reports/review-studio.html, reports/skill-interpretation.html, reports/skill-overview.htmlDeliverables: SKILL.md, agents/interface.yaml, reports/skill-ir.json, reports/compiled_targets.md, reports/output_quality_scorecard.md, reports/conformance_matrix.md, reports/security_trust_report.md, reports/skill_atlas.html, reports/registry_audit.md, reports/package_verification.md, reports/install_simulation.md, reports/upgrade_check.md, reports/adoption_drift_report.md, reports/review_waivers.md, reports/review_annotations.md, reports/review-studio.html, reports/skill-interpretation.html, reports/skill-overview.html
+
+
交付流程Delivery Flow输入材料Input materialSkill 包体Skill package可复用能力Reusable capability
交付流程把用户输入、生成的包体和可复用能力放在一条线上。The delivery flow places user input, generated package, and reusable capability on one path.
+
+
+
+ +
+
+
+

总览指标Metrics

+

分数来自本地文件和 reports 证据,缺失时明确标为证据不足。Scores are derived from package files and reports; missing inputs are shown as evidence gaps.

+
+
+

指标判读Reading

+

先看雷达图判断能力短板,再看每项分数的证据原因。分数不是装饰数字,必须和本地文件、reports 证据或证据不足提示对应。Read the radar first for weak spots, then inspect each score with its evidence. Scores must map to local files, reports, or explicit evidence gaps.

+
+
+
+
+
+
评分雷达Rating Radar完整度Completeness触发清晰Trigger clarity证据充分Evidence depth可维护性Maintainability可迁移性Portability
评分雷达展示结构完整度、触发边界、证据、维护和迁移的相对强弱。The radar chart compares completeness, trigger clarity, evidence, maintainability, and portability.
+
+

成熟度条Maturity Bar

+
  1. 稳定Stable完整度Completeness92SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
  2. 稳定Stable触发清晰Trigger clarity100frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
  3. 关注Watch证据充分Evidence depth60已生成 12 / 20 类报告证据。Generated 12 / 20 evidence report types.
  4. 稳定Stable可维护性Maintainability85SKILL.md 约 864 个词/字。SKILL.md is about 864 words/characters.
  5. 稳定Stable可迁移性Portability100agents/interface.yaml 已存在。agents/interface.yaml exists.
  6. 可用Usable上下文成本Context cost78入口约 864 个词/字,references 约 565 个词/字。Entrypoint is about 864 words/characters; references are about 565.
+
+
+
完整度Completeness92
  • SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
  • README.md 已存在,便于人工阅读。README.md exists for human-readable usage.
  • agents/interface.yaml 已存在,便于跨平台适配。agents/interface.yaml exists for cross-platform adaptation.
触发清晰Trigger clarity100
  • frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
  • description 有足够长度说明任务边界。The description is long enough to explain the task boundary.
  • description 已包含使用场景或排除边界信号。The description includes usage-scenario or exclusion-boundary signals.
证据充分Evidence depth60
  • 已生成 12 / 20 类报告证据。Generated 12 / 20 evidence report types.
  • skill-ir.json 已存在。skill-ir.json exists.
  • compiled_targets.json 已存在。compiled_targets.json exists.
可维护性Maintainability85
  • SKILL.md 约 864 个词/字。SKILL.md is about 864 words/characters.
  • 入口文件保持克制,可维护性较好。The entrypoint stays restrained, which supports maintainability.
  • references/ 已承载扩展指导。references/ carries extended guidance.
可迁移性Portability100
  • agents/interface.yaml 已存在。agents/interface.yaml exists.
  • manifest.json 已存在。manifest.json exists.
  • 目标平台或 adapter target 已声明。Target platforms or adapter targets are declared.
上下文成本Context cost78
  • 入口约 864 个词/字,references 约 565 个词/字。Entrypoint is about 864 words/characters; references are about 565.
  • 分数越高代表上下文成本越低。A higher score means lower context cost.
  • 上下文成本处于可控区间。Context cost is within a controlled range.
+
+
+
+ +
+
+
+

能力画像Capability

+

判断这个 Skill 在能力地图中的位置和复用范围。Places this skill on a capability map and clarifies reuse scope.

+
+
+
能力矩阵Capability Matrix执行确定性Execution certainty知识密度Knowledge densityCreative generationCreative generation
能力矩阵说明这个 Skill 更偏知识密集还是执行确定。The capability matrix shows whether the skill leans toward knowledge density or execution certainty.
+
+

画像摘要Profile

+
  • 能力类型:Creative generationCapability type: Creative generation
  • 成熟度:productionMaturity: production
  • 触发强度:手动触发 + description 路由Trigger strength: Manual activation plus description-based routing
  • 复用范围:跨平台Reuse scope: Cross-platform
+
+
+
+
+ +
+
+
+

原理结构Principle

+

说明入口、参考、脚本、评估和报告如何组成一个稳定闭环。Explains how entrypoint, references, scripts, evals, and reports form a stable loop.

+
+
+
分层结构Layered Structure入口层Entrypoint layer参考层Reference layer脚本层Script layer评估层Evaluation layer报告层Report layer
分层结构展示入口、参考、脚本、评估和报告如何各司其职。The layered structure shows how entrypoint, references, scripts, evals, and reports each carry a distinct role.
+
+
+

执行流程Execution Flow

+
  1. 读取用户输入,识别目标受众、内容渠道、关键词、品牌边界、禁止表达和必须覆盖的信息。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
  2. 对照 `references/brief-structure.md` 生成简报骨架:目标、洞察、角度、关键词、结构、证据、风险和验收标准。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
  3. 如果缺少核心输入,先提出最多三个澄清问题,不用假设补全用户没有提供的事实。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
  4. 用 `evals/trigger_cases.jsonl` 检查当前请求是否属于 GEO 内容简报场景。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
  5. 用 `evals/output_cases.jsonl` 检查输出是否包含输入摘要、内容结构、证据要求、禁区和下一步动作。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
+
+
+

调用方式How To Use

+
  1. 当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
  2. Use this skill when the request matches: 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。Use this skill when the request matches the frontmatter description.
+
+
+
+
+
+ +
+
+
+

契约边界Contract

+

把触发、输入、输出和排除场景放在同一屏。Keeps trigger, inputs, outputs, and exclusions on the same screen.

+
+
+
+

触发描述Trigger

+

将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.

+

输入材料Inputs

+
  • 用户提供的工作流、提示词、文档、记录或散乱笔记User-provided workflows, prompts, documents, records, or rough notes.
  • 期望沉淀的复用场景、排除项、约束和质量标准The reusable scenario, exclusions, constraints, and quality standards to capture.
+
+
+

输出结果Outputs

+
  • 可路由的 SKILL.mdA routeable SKILL.md.
  • agents/interface.yaml 元数据agents/interface.yaml metadata.
  • 必要的 references、scripts、evals、reports 证据Necessary references, scripts, evals, and reports evidence.
  • 结构化 Skill 目录,共 8 类关键资产。Structured skill directory with 8 key asset groups.
+

不应触发Should Not Trigger

+
  • 只需要一次性回答、没有复用价值的临时请求。One-off requests that do not need reusable skill behavior.
  • 要求直接执行相邻任务,而不是沉淀或使用这个 Skill。Requests to perform an adjacent task directly rather than create or use this skill.
  • 缺少必要事实且用户不允许澄清的场景。Cases that lack required facts and do not allow clarification.
+
+
+
+
+ +
+
+
+

质量评估Quality

+

展示强项、缺口和建议,避免只给分不解释。Shows strengths, gaps, and recommendations instead of scores without explanation.

+
+
+ + + +
类型Type证据Evidence建议Action
强项Strength触发面保持精简,并锚定在 frontmatter description。The trigger surface stays lean and anchored in the frontmatter description.保留并复用Keep
强项Strength已生成 Skill IR,核心语义可先于平台打包被审查和迁移。Skill IR is generated so core semantics can be reviewed and migrated before platform packaging.保留并复用Keep
强项Strength已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。Target compilation evidence is generated to review how IR maps to OpenAI, Claude, generic, and other target contracts.保留并复用Keep
缺口Gap证据充分需要补强:已生成 12 / 20 类报告证据。Evidence depth needs improvement: Generated 12 / 20 evidence report types.纳入下一轮修复Fix next
+
+
+

执行证据Execution Evidence

+
  • 尚未生成输出执行证据报告。The output execution evidence report has not been generated yet.
+
+
+

盲评审定Blind Adjudication

+
  • 尚未生成盲评审定报告。The blind review adjudication report has not been generated yet.
+
+
+
+
+

评审原则Review Rule

+
  • 先记录 reviewer 对 A/B 的选择,再打开答案 key 计算一致率。Record the reviewer's A/B choice before opening the answer key and calculating agreement.
  • 缺少真实 reviewer 决策时只显示待评审,不伪造人工结论。When real reviewer decisions are missing, show pending status instead of fabricating human conclusions.
+
+
+

运行原则Run Rule

+
  • recorded fixture 只能证明可复现样本,不等同于模型执行。A recorded fixture proves reproducible samples only; it is not model execution.
  • 只有 provider runner 返回 model metadata 时才计入 model-executed。Only provider runners that return model metadata count as model-executed.
+
+
+
+
+
+ +
+
+
+

风险治理Risk

+

提前暴露误触发、漂移、证据不足和迁移风险。Surfaces trigger, drift, evidence, and portability risks before the package grows.

+
+
+
风险热力Risk Heatmap000020210发生概率Probability影响程度Impact
风险热力图用影响程度和发生概率标出当前治理重点。The risk heatmap marks governance priorities by impact and probability.
+
+ + + +
风险Risk信号Signal应对Response
误触发风险Trigger riskfrontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
输出漂移风险Output drift risk已生成 12 / 20 类报告证据。Generated 12 / 20 evidence report types.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
证据不足风险Evidence gap risk已生成 12 / 20 类报告证据。Generated 12 / 20 evidence report types.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
包体膨胀风险Package bloat riskSKILL.md 约 864 个词/字。SKILL.md is about 864 words/characters.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
跨平台迁移风险Portability riskagents/interface.yaml 已存在。agents/interface.yaml exists.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
+
+
+

世界证据World Evidence

未生成 world-class ledger;当前报告不会宣称世界级完成。No world-class ledger was generated; this report does not claim world-class completion.

证据待补Evidence pending
待补证据Pending0仍需外部或人工证据接受。External or human evidence still needs acceptance.
已接受Accepted0已通过 source check 与提交契约。Passed source checks and submission contract.
源检查Source Checks0 / 0通过数 / 总检查数。Passed checks / total checks.

尚未生成 world-class ledger;这里只保留反过度承诺提示。No world-class ledger has been generated; this panel keeps the anti-overclaim guard visible.

+
+
+ +
+
+
+

包体资产Assets

+

让 reviewer 快速确认关键文件、目录和资产分布。Lets reviewers confirm key files, directories, and asset distribution quickly.

+
+
+
资产分布Asset Distribution42项42 itemsSKILL.mdSKILL.mdREADME.mdREADME.mdagents/interface.yamlagents/interface.yamlmanifest.jsonmanifest.jsonreferencesreferencesscriptsscripts
资产分布图展示当前包体的文件和目录重心。The asset distribution chart shows where files and directories are concentrated.
+ + + +
路径Path作用Role类型Type
SKILL.mdSkill 入口文件Skill entrypoint文件file
README.md人类可读使用说明Human-readable usage guide文件file
agents/interface.yaml跨平台接口元数据Neutral interface metadata文件file
manifest.json生命周期与打包元数据Lifecycle and portability metadata文件file
references扩展指导与复用资料Extended guidance and reusable notes目录folder
scripts确定性脚本或本地工具Deterministic helpers or local tooling目录folder
evals触发与质量检查Trigger and quality checks目录folder
reports生成的证据与总结报告Generated evidence and overview artifacts目录folder
+
+
+
+ +
+
+
+

迭代路线Roadmap

+

把下一步升级收束为少数高价值动作。Keeps next iteration moves focused and actionable.

+
+
+
迭代时间Iteration Timeline下一步 1Next 1Tighten trigger a…Tighten trigger a…下一步 2Next 2Add the first exe…Add the first exe…下一步 3Next 3Promote from scaf…Promote from scaf…
迭代时间线把下一步升级收束成少数可执行动作。The iteration timeline narrows the next upgrade into a few executable moves.
+
下一步 1Next 1

收紧触发与排除边界Tighten trigger and exclusions

在继续扩展前,需要先把相邻但不应触发的场景说清楚。The package needs clearer near-neighbor exclusions before it grows.

  • 增加 3 到 5 个应触发和不应触发的例子。Add 3 to 5 should-trigger and should-not-trigger examples.
  • 精炼 frontmatter description,明确重复任务和非目标。Refine the frontmatter description to name the recurring job and non-goals.
  • 扩展包体前先跑一轮触发评估。Run a first trigger evaluation pass before expanding the package.

路由更清晰,误触发更少。Cleaner routing and fewer accidental activations.

下一步 2Next 2

补上第一个执行资产Add the first execution asset

当前包体仍偏文本说明,应先增加一个能减少重复人工操作的资产。The package is still mostly prose. Add one asset that removes repeated manual work.

  • 如果用户会反复使用某段流程说明,把它沉淀到 references。Move stable procedural guidance into references if users will need it repeatedly.
  • 如果某个重复步骤可以执行而不是描述,就沉淀成一个确定性 helper script。Create one deterministic helper script if a repeated step can be executed instead of described.
  • 保持主 SKILL.md 简洁,并围绕路由与入口组织。Keep the main SKILL.md compact and route-oriented.

在不膨胀入口文件的前提下提升执行质量。Stronger execution quality without bloating the entrypoint.

下一步 3Next 3

从脚手架推进到生产可用Promote from scaffold to production-ready

第一版已经存在,下一步收益通常来自补上最小但有效的质量门禁。The first version exists; the next gain usually comes from adding the smallest useful gates.

  • 判断这个 Skill 是个人使用、团队复用,还是库级基础能力。Decide whether this skill is personal, team-reused, or library-grade.
  • 只添加与风险等级匹配的质量门禁。Add only the gates that match that risk level.
  • 一旦进入真实复用,就记录生命周期元数据和评审节奏。Record lifecycle metadata and review cadence once reuse becomes real.

更清晰地从探索性包体走向可维护资产。A clearer path from exploratory package to maintained asset.

+
+
+
+
+ + + diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.json new file mode 100644 index 0000000..968c3b5 --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/skill-overview.json @@ -0,0 +1,1060 @@ +{ + "skill_summary": { + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "display_name": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "maturity": "production", + "updated_at": "2026-06-15", + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": [ + "SKILL.md", + "agents/interface.yaml", + "reports/skill-ir.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-interpretation.html", + "reports/skill-overview.html" + ], + "flow": [ + "输入材料", + "Skill 包体", + "可复用能力" + ] + }, + "scorecard": { + "completeness_score": { + "label": "完整度", + "score": 92, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 60, + "reasons": [ + "已生成 12 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。", + "证据不足:缺少 output_quality_scorecard.json, conformance_matrix.json, security_trust_report.json。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 85, + "reasons": [ + "SKILL.md 约 864 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 证据不足,重复执行逻辑可能仍靠人工。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 78, + "reasons": [ + "入口约 864 个词/字,references 约 565 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本处于可控区间。" + ] + } + }, + "capability_profile": { + "archetype": "production", + "task_family": "Creative generation", + "maturity": "production", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "principle_model": { + "nodes": [ + { + "title": "意图澄清", + "body": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Ask one focused question and update intent context before adding assets." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "contract_boundary": { + "trigger": { + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "用户提供的工作流、提示词、文档、记录或散乱笔记", + "期望沉淀的复用场景、排除项、约束和质量标准" + ], + "outputs": [ + "可路由的 SKILL.md", + "agents/interface.yaml 元数据", + "必要的 references、scripts、evals、reports 证据", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "已有原始资料,但还没有清晰输入、输出和后续迭代路径。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "quality_review": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。", + "已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。", + "已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。" + ], + "gaps": [ + "证据充分需要补强:已生成 12 / 20 类报告证据。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers." + ] + }, + "prompt_quality": { + "overall_quality_score": 85.0, + "highlights": [ + "Primary prompt task family: Creative generation.", + "Complexity: complex — multiple inputs, constraints, or task families require tradeoff handling", + "Completeness: 80/100.", + "Clarity: 90/100." + ] + }, + "system_model": { + "stability": { + "score": 77, + "band": "stable-first-pass" + }, + "highlights": [ + "Stability: stable-first-pass (77/100).", + "Owned job: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "Leverage: Clarify the real job boundary — Ask one focused question and update intent context before adding assets.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language." + ] + } + }, + "risk_governance": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 2, + "signal": "已生成 12 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 2, + "signal": "已生成 12 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 2, + "signal": "SKILL.md 约 864 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "table", + "report", + "brief" + ], + "score": 4, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "screenshot", + "visual", + "screen" + ], + "score": 3, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "citation", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "copy", + "content" + ], + "score": 2, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + }, + { + "key": "tutorial_quality", + "label": "Tutorial quality", + "matched_keywords": [ + "tutorial" + ], + "score": 1, + "risks": [ + "Generic section headings make the tutorial feel templated instead of fitted to the learner's task.", + "Steps may explain what to do without naming the exact check that proves the step worked.", + "Examples can become too abstract when the input material is concrete." + ], + "constraints": [ + "Write headings from the user's domain nouns and desired outcome, not from generic labels like Overview or Key Points.", + "Pair each major step with a visible success check or expected intermediate output.", + "Use one concrete worked example before adding broad principles." + ], + "self_repair": [ + "Replace generic H2/H3 headings with task-specific headings before final output.", + "Scan every numbered step for a missing verification cue." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "world_class_readiness": { + "ready": false, + "decision": "not-generated", + "entry_count": 0, + "pending_count": 0, + "accepted_count": 0, + "external_pending_count": 0, + "human_pending_count": 0, + "source_check_count": 0, + "source_pass_count": 0, + "conclusion_zh": "未生成 world-class ledger;当前报告不会宣称世界级完成。", + "conclusion_en": "No world-class ledger was generated; this report does not claim world-class completion.", + "entries": [] + }, + "package_assets": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 2 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 0 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 2 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 34 + } + ], + "file_count": 42, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 2 + }, + { + "label": "scripts", + "value": 1 + }, + { + "label": "evals", + "value": 2 + }, + { + "label": "reports", + "value": 34 + } + ] + }, + "iteration_roadmap": { + "items": [ + { + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations." + }, + { + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint." + }, + { + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset." + } + ] + }, + "report_contract": { + "schema_version": "2.0", + "html_report": "reports/skill-overview.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "layout": "kami-white-audit-v2", + "nav_labels": [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线" + ], + "nav_labels_en": [ + "Overview", + "Metrics", + "Profile", + "Principle", + "Contract", + "Quality", + "Risk", + "Assets", + "Roadmap" + ] + }, + "name": "geo-content-brief-skill", + "title": "Geo Content Brief Skill", + "display_name": "Geo Content Brief Skill", + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "logic_steps": [ + "读取用户输入,识别目标受众、内容渠道、关键词、品牌边界、禁止表达和必须覆盖的信息。", + "对照 `references/brief-structure.md` 生成简报骨架:目标、洞察、角度、关键词、结构、证据、风险和验收标准。", + "如果缺少核心输入,先提出最多三个澄清问题,不用假设补全用户没有提供的事实。", + "用 `evals/trigger_cases.jsonl` 检查当前请求是否属于 GEO 内容简报场景。", + "用 `evals/output_cases.jsonl` 检查输出是否包含输入摘要、内容结构、证据要求、禁区和下一步动作。" + ], + "usage_steps": [ + "当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "Use this skill when the request matches: 将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。" + ], + "package_map": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 2 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 0 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 2 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 34 + } + ], + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。", + "已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。", + "已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。" + ], + "scenarios": [ + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:当你需要把访谈纪要、关键词、竞品线索和渠道限制整理成中文 GEO 内容简报时,使用 $geo-content-brief-skill。", + "已有原始资料,但还没有清晰输入、输出和后续迭代路径。" + ], + "trigger_contract": { + "description": "将 GEO 内容访谈、关键词笔记、竞品线索和渠道约束整理成可执行的中文内容简报。用于团队复用、选题规划、内容生产交接和质量评审;不用于直接代写完整长文或替代品牌策略判断。", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "io_contract": { + "inputs": [ + "用户提供的工作流、提示词、文档、记录或散乱笔记", + "期望沉淀的复用场景、排除项、约束和质量标准" + ], + "outputs": [ + "可路由的 SKILL.md", + "agents/interface.yaml 元数据", + "必要的 references、scripts、evals、reports 证据", + "结构化 Skill 目录,共 8 类关键资产。" + ] + }, + "principles": [ + { + "title": "意图澄清", + "body": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。" + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Ask one focused question and update intent context before adding assets." + } + ], + "roadmap": [ + { + "title": "Tighten trigger and exclusions", + "why": "The package needs clearer near-neighbor exclusions before it grows.", + "actions": [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package." + ], + "unlocks": "Cleaner routing and fewer accidental activations." + }, + { + "title": "Add the first execution asset", + "why": "The package is still mostly prose. Add one asset that removes repeated manual work.", + "actions": [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented." + ], + "unlocks": "Stronger execution quality without bloating the entrypoint." + }, + { + "title": "Promote from scaffold to production-ready", + "why": "The first version exists; the next gain usually comes from adding the smallest useful gates.", + "actions": [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real." + ], + "unlocks": "A clearer path from exploratory package to maintained asset." + } + ], + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。" + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": "2.0.0", + "source_path": "reports/skill-ir.json", + "target_count": 5, + "trigger_samples": 1, + "output_eval_cases": 0 + }, + "compiled_targets": { + "ok": true, + "schema_version": "1.0", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter_mode": "metadata-adapter", + "degradation_strategy": "metadata-adapter", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "permission_enforcement": "metadata-only", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "adapter_mode": "neutral-source-plus-adapter", + "degradation_strategy": "neutral-source-plus-adapter", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "permission_enforcement": "metadata-fallback", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [] + }, + { + "target": "generic", + "status": "pass", + "adapter_mode": "agent-skills-compatible", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills compatible neutral package", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "targets/generic/adapter.json" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "agent-skills-compatible", + "status": "pass", + "adapter_mode": "neutral-agent-skills-source", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills standard source tree", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "vscode", + "status": "pass", + "adapter_mode": "vscode-agent-skills-adapter", + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "permission_enforcement": "client-or-workspace-trust", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [] + } + ], + "failures": [], + "warnings": [] + }, + "output_quality": {}, + "output_execution": { + "ok": false, + "summary": {}, + "runner": {}, + "failures": [] + }, + "output_blind_review": { + "summary": {}, + "seed": "", + "pair_count": 0, + "answer_key_separate": false + }, + "output_review_kit": { + "ok": false, + "summary": {}, + "artifacts": {}, + "failures": [] + }, + "output_review_adjudication": { + "ok": false, + "summary": {}, + "reviewer": "", + "reviewed_at": "", + "failures": [] + }, + "benchmark_reproducibility": { + "ok": false, + "summary": {}, + "commit": "", + "missing_artifacts": [], + "limitations": [] + }, + "runtime_conformance": {}, + "runtime_permissions": { + "ok": false, + "summary": {}, + "expected_capabilities": [], + "targets": [], + "failures": [] + }, + "trust_security": {}, + "skill_atlas": {}, + "registry_distribution": { + "ok": false, + "package": {}, + "failures": [], + "warnings": [] + }, + "package_verification": { + "ok": false, + "summary": {}, + "failures": [], + "warnings": [] + }, + "install_simulation": { + "ok": false, + "summary": {}, + "failures": [], + "warnings": [] + }, + "upgrade_check": { + "ok": false, + "summary": {}, + "upgrade_diff": {}, + "release_notes": [], + "failures": [], + "warnings": [] + }, + "adoption_drift": { + "ok": true, + "summary": { + "event_count": 0, + "adoption_sample_count": 0, + "activation_count": 0, + "accepted_count": 0, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "no-data", + "event_types": {}, + "failure_types": {}, + "source_types": {}, + "command_counts": {} + }, + "next_iteration_candidates": [ + { + "signal": "no telemetry", + "recommendation": "Start with a small metadata-only sample before using telemetry for release decisions." + } + ], + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "failures": [] + }, + "review_waivers": { + "ok": true, + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 0, + "waiverable_open_count": 0, + "non_waivable_count": 0 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "known_gate_keys": [ + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ] + }, + "failures": [], + "warnings": [] + }, + "review_annotations": { + "ok": true, + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [] + }, + "world_class_evidence_plan": { + "ok": false, + "summary": {}, + "tasks": [], + "source_audit": {} + }, + "world_class_evidence_ledger": { + "ok": false, + "summary": {}, + "entries": [], + "source_plan": {} + }, + "synthesis_highlights": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers." + ] + }, + "prompt_quality": { + "overall_quality_score": 85.0, + "highlights": [ + "Primary prompt task family: Creative generation.", + "Complexity: complex — multiple inputs, constraints, or task families require tradeoff handling", + "Completeness: 80/100.", + "Clarity: 90/100." + ] + }, + "system_model": { + "stability": { + "score": 77, + "band": "stable-first-pass" + }, + "highlights": [ + "Stability: stable-first-pass (77/100).", + "Owned job: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "Leverage: Clarify the real job boundary — Ask one focused question and update intent context before adding assets.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language." + ] + }, + "metadata": { + "canonical_format": "agent-skills", + "targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "maturity_tier": "production", + "skill_archetype": "production", + "updated_at": "2026-06-15" + } +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.json b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.json new file mode 100644 index 0000000..5a2417a --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.json @@ -0,0 +1,166 @@ +{ + "skill_name": "geo-content-brief-skill", + "description": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "systems_doctrine": "Structure drives behavior: improve the boundary, feedback loops, drift watch, and leverage points before adding weight.", + "stability": { + "score": 77, + "band": "stable-first-pass" + }, + "boundary_map": { + "owned_job": "将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。", + "input_boundary": [ + "user-provided workflow notes, prompts, docs, or examples" + ], + "output_boundary": "a reusable skill output", + "non_goals": [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included" + ], + "constraints": [], + "standards": [], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "maturity_assumption": "scaffold" + }, + "feedback_loops": [ + { + "name": "Intent boundary loop", + "signal": "Intent confidence score is 10/100.", + "response": "Ask only the highest-leverage clarification before adding package weight.", + "evidence": "reports/intent-confidence.md and reports/intent-dialogue.md" + }, + { + "name": "Reference synthesis loop", + "signal": "Benchmark patterns are useful only after they are abstracted into borrow and avoid guidance.", + "response": "Borrow one pattern at a time and keep the rest as reviewer-visible evidence.", + "evidence": "reports/reference-synthesis.md", + "current_patterns": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package.", + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier." + ] + }, + { + "name": "Output quality loop", + "signal": "Generated output may fail in recurring domain-specific ways.", + "response": "Apply predicted output-risk families as self-repair checks before final output.", + "evidence": "reports/output-risk-profile.md", + "current_risk_families": [ + "Markdown readability", + "Screenshot and visual capture", + "Citation and footnote clutter", + "Tone and specificity", + "Tutorial quality" + ] + }, + { + "name": "Reviewer feedback loop", + "signal": "Human review catches drift that static checks miss.", + "response": "Capture lightweight feedback and turn repeated findings into gates or references.", + "evidence": "reports/review-viewer.html and feedback records" + }, + { + "name": "Lifecycle loop", + "signal": "As reuse grows, the skill needs stronger gates, ownership, and regression evidence.", + "response": "Promote only when the next gate improves reliability more than context cost.", + "evidence": "manifest.json, reports/iteration-directions.md, and governance checks" + } + ], + "drift_watch": [ + { + "name": "Trigger drift", + "watch_signal": "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "countermeasure": "Add near-neighbor exclusions and route evals before expanding workflow steps.", + "cadence": "per trigger or description change" + }, + { + "name": "Output drift", + "watch_signal": "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "countermeasure": "Refresh output-risk and artifact-design profiles, then add one self-repair check.", + "cadence": "after the first 3-5 real uses", + "risk_families": [ + "Markdown readability", + "Screenshot and visual capture", + "Citation and footnote clutter", + "Tone and specificity", + "Tutorial quality" + ] + }, + { + "name": "Reference drift", + "watch_signal": "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "countermeasure": "Re-run reference synthesis and keep only patterns that improve the current boundary.", + "cadence": "per material benchmark or product assumption change" + }, + { + "name": "Governance drift", + "watch_signal": "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal.", + "countermeasure": "Promote maturity tier and add reviewer-visible lifecycle evidence.", + "cadence": "when reuse becomes real" + } + ], + "failure_pattern_map": [ + { + "family": "Boundary failure", + "symptom": "The skill handles nearby requests that were never part of the recurring job.", + "repair": "Narrow the description and add explicit non-goals before adding more execution steps." + }, + { + "family": "Feedback gap", + "symptom": "The skill has rules but no signal telling authors which rule should change after use.", + "repair": "Turn repeated reviewer feedback into one eval, one reference note, or one self-repair check." + }, + { + "family": "Output degradation", + "symptom": "The result is structurally correct but generic, cluttered, or weakly matched to the user's domain.", + "repair": "Use output-risk families as pre-final checks.", + "current_risk_families": [ + "Markdown readability", + "Screenshot and visual capture", + "Citation and footnote clutter", + "Tone and specificity", + "Tutorial quality" + ] + }, + { + "family": "Prompt-behavior mismatch", + "symptom": "The role, task, and format are copied from a prompt instead of becoming stable skill behavior.", + "repair": "Convert reusable role/task/format assumptions into workflow, reports, or references.", + "watch_axes": [ + "Specificity" + ] + } + ], + "leverage_points": [ + { + "rank": 1, + "point": "Clarify the real job boundary", + "why": "Intent uncertainty creates downstream trigger, output, and governance errors.", + "move": "Ask one focused question and update intent context before adding assets." + }, + { + "rank": 2, + "point": "Tune the frontmatter description", + "why": "The description is the highest-leverage routing surface.", + "move": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + }, + { + "rank": 3, + "point": "Install output self-repair checks", + "why": "The likely failure families are: Markdown readability, Screenshot and visual capture, Citation and footnote clutter.", + "move": "Add only the checks that prevent recurring output mistakes." + }, + { + "rank": 4, + "point": "Borrow one pattern, not a whole product", + "why": "External references improve quality when reduced to structure, not copied as surface style.", + "move": "Start from: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts." + } + ], + "reviewer_rule": "Reviewer should ask whether the skill's structure will keep producing the desired behavior after repeated real use." +} \ No newline at end of file diff --git a/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.md b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.md new file mode 100644 index 0000000..1b59b0a --- /dev/null +++ b/.previews/yao-meta-skill-v2-report-demo/geo-content-brief-skill/reports/system-model.md @@ -0,0 +1,156 @@ +# System Model + +Skill: `geo-content-brief-skill` + +- Stability score: `77/100` +- Stability band: `stable-first-pass` +- Doctrine: Structure drives behavior: improve the boundary, feedback loops, drift watch, and leverage points before adding weight. + +## System Boundary Map + +- Owned job: 将 GEO 内容访谈、关键词笔记和渠道约束整理成可执行的中文内容简报 Skill,适用于团队复用、触发边界审查和后续质量评估。 +- Output boundary: a reusable skill output +- Maturity assumption: `scaffold` +- Input boundary: + - user-provided workflow notes, prompts, docs, or examples +- Non-goals: + - one-off adjacent requests that do not match the recurring job + - private local material that was not intentionally included +- Human judgment boundary: + - Ask one focused clarification when the real job, output, or exclusion boundary is unclear. + - Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints. + - Do not silently broaden the skill into adjacent jobs just because the examples are nearby. + +## Feedback Loops + +### Intent boundary loop + +- Signal: Intent confidence score is 10/100. +- Response: Ask only the highest-leverage clarification before adding package weight. +- Evidence: reports/intent-confidence.md and reports/intent-dialogue.md + +### Reference synthesis loop + +- Signal: Benchmark patterns are useful only after they are abstracted into borrow and avoid guidance. +- Response: Borrow one pattern at a time and keep the rest as reviewer-visible evidence. +- Evidence: reports/reference-synthesis.md +- Current patterns: + - Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. + - Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. + - Borrow the discipline of defining what the skill should not own before growing the package. + - Do not let packaging or platform concerns swallow the core job boundary. + - Do not create experimental overhead that exceeds the skill's real risk tier. + +### Output quality loop + +- Signal: Generated output may fail in recurring domain-specific ways. +- Response: Apply predicted output-risk families as self-repair checks before final output. +- Evidence: reports/output-risk-profile.md +- Current risk families: + - Markdown readability + - Screenshot and visual capture + - Citation and footnote clutter + - Tone and specificity + - Tutorial quality + +### Reviewer feedback loop + +- Signal: Human review catches drift that static checks miss. +- Response: Capture lightweight feedback and turn repeated findings into gates or references. +- Evidence: reports/review-viewer.html and feedback records + +### Lifecycle loop + +- Signal: As reuse grows, the skill needs stronger gates, ownership, and regression evidence. +- Response: Promote only when the next gate improves reliability more than context cost. +- Evidence: manifest.json, reports/iteration-directions.md, and governance checks + +## Delay And Drift Watch + +### Trigger drift + +- Watch signal: Users start invoking the skill for adjacent one-off or explanation-only requests. +- Countermeasure: Add near-neighbor exclusions and route evals before expanding workflow steps. +- Cadence: per trigger or description change + +### Output drift + +- Watch signal: Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain. +- Countermeasure: Refresh output-risk and artifact-design profiles, then add one self-repair check. +- Cadence: after the first 3-5 real uses +- Risk families: + - Markdown readability + - Screenshot and visual capture + - Citation and footnote clutter + - Tone and specificity + - Tutorial quality + +### Reference drift + +- Watch signal: Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff. +- Countermeasure: Re-run reference synthesis and keep only patterns that improve the current boundary. +- Cadence: per material benchmark or product assumption change + +### Governance drift + +- Watch signal: Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal. +- Countermeasure: Promote maturity tier and add reviewer-visible lifecycle evidence. +- Cadence: when reuse becomes real + +## Failure Pattern Map + +### Boundary failure + +- Symptom: The skill handles nearby requests that were never part of the recurring job. +- Repair: Narrow the description and add explicit non-goals before adding more execution steps. + +### Feedback gap + +- Symptom: The skill has rules but no signal telling authors which rule should change after use. +- Repair: Turn repeated reviewer feedback into one eval, one reference note, or one self-repair check. + +### Output degradation + +- Symptom: The result is structurally correct but generic, cluttered, or weakly matched to the user's domain. +- Repair: Use output-risk families as pre-final checks. +- Current Risk Families: + - Markdown readability + - Screenshot and visual capture + - Citation and footnote clutter + - Tone and specificity + - Tutorial quality + +### Prompt-behavior mismatch + +- Symptom: The role, task, and format are copied from a prompt instead of becoming stable skill behavior. +- Repair: Convert reusable role/task/format assumptions into workflow, reports, or references. +- Watch Axes: + - Specificity + +## Highest Leverage Moves + +### 1. Clarify the real job boundary + +- Why: Intent uncertainty creates downstream trigger, output, and governance errors. +- Move: Ask one focused question and update intent context before adding assets. + +### 2. Tune the frontmatter description + +- Why: The description is the highest-leverage routing surface. +- Move: Name the recurring job, expected input, output, and strongest non-goal in compact language. + +### 3. Install output self-repair checks + +- Why: The likely failure families are: Markdown readability, Screenshot and visual capture, Citation and footnote clutter. +- Move: Add only the checks that prevent recurring output mistakes. + +### 4. Borrow one pattern, not a whole product + +- Why: External references improve quality when reduced to structure, not copied as surface style. +- Move: Start from: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. + +## Reviewer Use + +- Reviewer should ask whether the skill's structure will keep producing the desired behavior after repeated real use. +- Prefer changing the system boundary, feedback loop, or leverage point before adding more prose. +- If a problem repeats, convert it into a named failure pattern and one regression check. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..50eeb9d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,149 @@ +# Agent Instructions + +## Project Map + +`yao-meta-skill` is the source repository for the Yao Meta Skill / Skill OS. Keep the root `SKILL.md` lean and route depth through the existing project layers: + +- `SKILL.md`: public trigger surface, compact workflow, and Skill OS gates. +- `references/`: stable method doctrine and operating guidance. +- `scripts/`: executable logic, report generators, compilers, gates, and CLI entrypoints. +- `evals/`: trigger, output, packaging, semantic, and regression fixtures. +- `skill-ir/`: platform-neutral skill contract and examples. +- `agents/interface.yaml`: portable runtime and trust metadata. +- `security/`: script, dependency, network, permission, and trust policies. +- `registry/`: package metadata, installability evidence, and compatibility metadata. +- `skill_atlas/`: portfolio catalog, route overlap, ownership, stale-skill, and dependency evidence. +- `reports/`: generated review, trust, conformance, registry, overview, and release evidence. +- `docs/`: durable public docs such as migration notes and localized READMEs. + +## Verification + +Use `make ci-test` as the default full verification command before calling a change done. For focused work, run the smallest relevant target first, then finish with `make ci-test` when generated artifacts, packaging, registry, trust, or report UI changed. + +Common focused checks: + +- CLI changes: `python3 tests/verify_yao_cli.py` +- Operator UX changes: `python3 tests/verify_operator_ux.py` +- Skill overview report changes: `python3 tests/verify_skill_overview.py` +- Review Studio changes: `python3 tests/verify_review_studio.py` +- Trust or script inventory changes: `python3 tests/verify_trust_check.py` +- Packaging or registry changes: `python3 tests/verify_package_verification.py && python3 tests/verify_registry_audit.py` + +After source changes that affect scripts, package contents, trust evidence, Review Studio, registry metadata, or generated reports, refresh the release evidence before final sign-off: + +```bash +GENERATED_AT="${GENERATED_AT:-$(date +%F)}" +python3 scripts/run_output_execution.py --runner-command '["python3","scripts/local_output_eval_runner.py"]' +python3 scripts/compile_skill.py . --generated-at "$GENERATED_AT" +python3 scripts/cross_packager.py . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip +python3 scripts/simulate_install.py . --package-dir dist --install-root dist/install-simulation --output-json reports/install_simulation.json --output-md reports/install_simulation.md --generated-at "$GENERATED_AT" +python3 scripts/trust_check.py . --output-json reports/security_trust_report.json --output-md reports/security_trust_report.md +python3 scripts/registry_audit.py . --generated-at "$GENERATED_AT" +python3 scripts/verify_package.py . --package-dir dist --expectations evals/packaging_expectations.json --registry-json reports/registry_audit.json --output-json reports/package_verification.json --output-md reports/package_verification.md --require-zip --generated-at "$GENERATED_AT" +python3 scripts/registry_audit.py . --generated-at "$GENERATED_AT" +python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json --current-package-json reports/registry_audit.json --output-json reports/upgrade_check.json --output-md reports/upgrade_check.md --generated-at "$GENERATED_AT" +python3 scripts/render_adoption_drift_report.py . --generated-at "$GENERATED_AT" +python3 scripts/render_architecture_maintainability.py . --generated-at "$GENERATED_AT" +python3 scripts/python_compat_check.py . --generated-at "$GENERATED_AT" +python3 scripts/probe_runtime_permissions.py . --package-dir dist +python3 scripts/render_review_waivers.py . --generated-at "$GENERATED_AT" +python3 scripts/render_review_annotations.py . +python3 scripts/build_skill_atlas.py --workspace-root . --output-dir skill_atlas --report-html reports/skill_atlas.html --report-json reports/skill_atlas.json --today "$GENERATED_AT" +python3 scripts/render_world_class_evidence_plan.py . --generated-at "$GENERATED_AT" +python3 scripts/render_world_class_evidence_ledger.py . --generated-at "$GENERATED_AT" +python3 scripts/render_world_class_evidence_intake.py . --generated-at "$GENERATED_AT" +python3 scripts/render_world_class_submission_review.py . --generated-at "$GENERATED_AT" +python3 scripts/render_world_class_operator_runbook.py . --generated-at "$GENERATED_AT" +python3 scripts/render_world_class_claim_guard.py . --generated-at "$GENERATED_AT" +python3 scripts/render_daily_skillops_report.py . --generated-at "$GENERATED_AT" +python3 scripts/render_weekly_curator_report.py . --generated-at "$GENERATED_AT" +python3 scripts/render_skill_os2_audit.py . --generated-at "$GENERATED_AT" +python3 scripts/render_skill_os2_coverage.py . --generated-at "$GENERATED_AT" +python3 scripts/render_context_reports.py --generated-at "$GENERATED_AT" +python3 scripts/render_benchmark_reproducibility.py . --generated-at "$GENERATED_AT" +python3 scripts/render_skill_overview.py . +python3 scripts/render_skill_interpretation.py . +python3 scripts/render_review_viewer.py . +python3 scripts/render_world_class_preflight.py . --generated-at "$GENERATED_AT" +python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json +python3 scripts/render_evidence_consistency.py . --generated-at "$GENERATED_AT" +``` + +For final release evidence, commit source and generated package evidence first, then run the clean-lock reports from a clean worktree: + +```bash +python3 scripts/render_context_reports.py --generated-at "$GENERATED_AT" +python3 scripts/render_benchmark_reproducibility.py . --generated-at "$GENERATED_AT" +python3 scripts/render_daily_skillops_report.py . --generated-at "$GENERATED_AT" +python3 scripts/render_weekly_curator_report.py . --generated-at "$GENERATED_AT" +python3 scripts/render_skill_overview.py . +python3 scripts/render_skill_interpretation.py . +python3 scripts/render_review_viewer.py . +python3 scripts/render_world_class_preflight.py . --generated-at "$GENERATED_AT" +python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json +python3 scripts/render_evidence_consistency.py . --generated-at "$GENERATED_AT" +``` + +If `reports/benchmark_reproducibility.json` reports `release_lock_ready: false`, do not commit that benchmark as release evidence. Restore the transient dirty-lock reports, commit the source/generated evidence that caused the dirty state, and regenerate the clean-lock reports on the resulting clean tree. + +Local sync into `~/.agents/skills.disabled/yao-meta-skill` or `~/.agents/skills/yao-meta-skill` must keep the install preflight enabled unless the user explicitly requests a diagnostic bypass. `make sync-local-install` and `make sync-active-install` rebuild the package first, then `scripts/sync_local_install.py` refuses to copy files when install simulation or installer permission enforcement fails. + +Clean test-only scratch directories after verification with `find tests -maxdepth 1 \( -name 'tmp' -o -name 'tmp_*' \) -type d -exec rm -rf {} +`. Do not clean unrelated untracked files. + +## Boundaries + +- Do not expand root `SKILL.md` with long method text. Add durable guidance to `references/` or executable behavior to `scripts/`. +- Do not commit private customer work, one-off business skills, or local research reports unless the user explicitly promotes them into examples, fixtures, or public evidence. +- Treat untracked files outside `tests/tmp_*` as user work. Do not delete, move, or overwrite them without explicit approval. +- Do not hand-edit generated evidence when a generator exists. Regenerate the source report instead. +- Do not introduce external chart or UI dependencies for static reports unless the user explicitly approves them. The current report pattern is static HTML, local CSS, and inline SVG. +- Keep package artifacts, registry checksums, install simulation, trust reports, overview reports, and Review Studio evidence in sync after source changes. + +## Hotspot Ownership + +- `scripts/yao.py`: unified CLI orchestration. Keep command behavior stable; move pure config and side-effect-free helpers into small internal modules. +- `scripts/render_skill_overview.py`: v2 bilingual skill overview report. Preserve `reports/skill-overview.html` / `.json`, `body data-report-lang="zh-CN"`, default Simplified Chinese, English switch, and inline-chart/no-external-dependency behavior. +- `scripts/skill_report_layout.py` plus `assets/skill-overview.css` / `assets/skill-overview.js`: overview report layout contract and inline assets. Keep the generated report single-file at render time, but keep long CSS/JS source in `assets/` instead of embedding it inside Python. +- `scripts/render_review_studio.py`: Review Studio gate orchestration. Keep gate scoring, evidence links, and action generation separate from layout helpers. +- `scripts/review_studio_layout.py` plus `assets/review-studio.css`: Review Studio static layout and CSS contract. Keep the generated report single-file at render time, but keep long CSS source in `assets/` instead of embedding it inside Python. +- `scripts/review_studio_formatting.py`: Review Studio dictionary-to-panel formatting and Chinese metric labels. +- `scripts/review_studio_gates.py`: Review Studio gate evaluation, release decision scoring, and gate status labels. +- `scripts/render_skill_os2_audit.py`: requirement-by-requirement Skill OS 2.0 completion audit. Keep local evidence, human-required gaps, and external-required gaps separate so reports do not overclaim world-class readiness. +- `scripts/render_world_class_evidence_plan.py`: executable evidence task plan for the remaining world-class readiness gaps. Keep provider, human, native-permission, and real-client telemetry evidence requirements concrete without marking planned work as complete. +- `scripts/render_world_class_evidence_ledger.py`: machine-checkable acceptance ledger for the remaining world-class evidence gaps. Keep anti-overclaim guards explicit so planned work, metadata fallbacks, pending review, and local command runners never count as final evidence. +- `scripts/render_world_class_evidence_intake.py`: intake validator for external and human world-class evidence packets. Real submissions must reference concrete local aggregate artifacts with matching SHA-256 digests; templates may stay hash-free and must not count as evidence. +- `scripts/world_class_evidence_contract.py`: shared intake contract and artifact-integrity validator. Keep ledger, intake, and submission review aligned so source evidence cannot be accepted without a valid real submission and matching artifact SHA-256 checks. +- `scripts/render_world_class_submission_review.py`: read-only queue for external and human evidence packets after intake validation. Keep it from accepting evidence; it may only compare packet validity, source evidence checks, and ledger state. +- `scripts/render_world_class_operator_runbook.py`: operator-facing world-class evidence runbook. Keep it as coordination guidance only; it must not accept evidence or flip world-class readiness. +- `scripts/render_world_class_preflight.py` plus `scripts/world_class_preflight_layout.py`: operator-facing collection preflight for world-class evidence. Keep data assembly and CLI emission in the renderer, keep HTML layout in the layout helper, keep environment and external prerequisite checks redacted, and never let preflight count as accepted evidence. +- `scripts/render_benchmark_reproducibility.py`: release-facing benchmark reproducibility manifest. Keep methodology sections, required artifacts, failure disclosure, reproduction commands, and world-class limitations machine-checkable. +- `scripts/skill_report_model.py`, `scripts/skill_report_metrics.py`, `scripts/skill_report_charts.py`: skill overview data model, scoring, and inline SVG chart generation. +- `scripts/yao_cli_config.py`: CLI target maps, archetype heuristics, diagnosis copy, and side-effect-free shaping helpers. +- `scripts/yao_cli_parser.py`: CLI argparse command surface, flags, choices, and command handler binding. +- `scripts/yao_cli_operator_commands.py`: read-only operator diagnostics for active install status, localized homepage sync, and PR review reports. Keep install checks non-mutating, docs sync explicit, and PR review free of merge/write actions. +- `scripts/yao_cli_parser_operator.py`: argparse surface for operator UX commands. Keep command names stable and route all behavior into `yao_cli_operator_commands.py`. +- `scripts/yao_cli_telemetry.py`: opt-in metadata-only CLI run telemetry. Keep it free of prompt, argument, output, transcript, note, or message capture. +- `scripts/import_telemetry_events.py`: external telemetry importer. Validate the whole input before appending events, and keep raw prompt/output/transcript/message/note fields blocked. +- `scripts/emit_telemetry_event.py`: external client telemetry emitter. It may append one normalized metadata event to a local spool, but must never accept or write raw prompt, output, transcript, message, note, argument, or private content. +- `scripts/render_telemetry_hook_recipes.py`: client hook recipe report. Keep recipes metadata-only, mark native auto-capture as unclaimed unless a real client integration exists, and preserve dry-run commands for Browser/Chrome/IDE/wrapper adapters. +- `scripts/telemetry_native_host.py`: Browser/Chrome Native Messaging telemetry bridge. Preserve length-prefixed stdio behavior, raw-content blocking, and launcher/manifest generation tests. + +New helper modules that are imported by CLI/report scripts but are not standalone commands must declare: + +```python +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by for ." +``` + +Then update `tests/verify_trust_check.py` so help-smoke coverage and trust reporting stay explicit. + +## Long-Running Work + +Stop and surface state instead of retrying when any of these happens: + +- Two consecutive checkpoints show no new files, no new passing test, and no new diagnosis. +- The same command fails with the same error three times. +- A required credential, network, package registry, or external service is unavailable. +- A generated package hash, registry checksum, install simulation, or trust summary cannot be reconciled after regeneration. + +When stopping, report the exact command, current `git status --short --branch -uall`, and the smallest next diagnostic. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..766e4f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Yao Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9fae821 --- /dev/null +++ b/Makefile @@ -0,0 +1,283 @@ +PYTHON ?= python3 +REQUIRED_PYTHON_VERSION ?= 3.11 +LOCAL_SKILL_INSTALL_DIR ?= $(HOME)/.agents/skills.disabled/yao-meta-skill +ACTIVE_SKILL_INSTALL_DIR ?= $(HOME)/.agents/skills/yao-meta-skill + +.PHONY: python-version-check eval eval-suite route-scorecard route-confusion-check description-optimization judge-blind-eval description-optimization-check promotion-check python-compat-check architecture-maintainability-check yao-cli-check yao-cli-world-class-check operator-ux-check skill-overview-check skill-interpretation-check skill-report-metrics-check skill-report-charts-check html-rendering-check skill-ir-check compiler-check output-eval-check output-execution-check output-review-kit-check output-review-adjudication-check runtime-conformance-check runtime-permission-check trust-check skill-atlas-check registry-audit-check package-verify-check install-simulation-check upgrade-check review-viewer-check review-studio-check skill-os2-audit-check skill-os2-coverage-check world-class-evidence-check world-class-ledger-check world-class-intake-check world-class-submission-kit-check world-class-preflight-check world-class-submission-review-check world-class-runbook-check world-class-claim-guard-check benchmark-reproducibility-check evidence-consistency-check feedback-check adaptation-safety-check skillops-opportunity-check daily-skillops-check weekly-curator-check adoption-drift-check telemetry-import-check telemetry-emit-check telemetry-hooks-check telemetry-native-host-check review-waivers-check review-annotations-check baseline-compare-check reference-scan-check github-benchmark-scan-check intent-confidence-check reference-synthesis-check output-risk-profile-check artifact-design-profile-check prompt-quality-profile-check system-model-check iteration-directions-check description-drift-history iteration-ledger results-panel regression-history context-reports portability-report portability-check failure-regression-check package-check package-failure-check security-boundary-check local-install-sync-check snapshot-check validate lint governance-check resource-boundary-check quality-check sync-local-install sync-active-install test ci-test clean + +python-version-check: + @$(PYTHON) -c 'import sys; required=tuple(map(int, "$(REQUIRED_PYTHON_VERSION)".split("."))); actual=sys.version_info[:2]; raise SystemExit(0 if actual >= required else "Python {}.{}+ is required; {}.{} is active. Set PYTHON=/path/to/python{} or create a Python {} virtualenv.".format(*required, *actual, "$(REQUIRED_PYTHON_VERSION)", "$(REQUIRED_PYTHON_VERSION)"))' + +eval: + $(PYTHON) scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json --baseline-description-file evals/baseline_description.txt + +eval-suite: + $(PYTHON) scripts/run_eval_suite.py + +route-scorecard: + $(PYTHON) scripts/build_confusion_matrix.py --history-snapshot-output evals/history/2026-04-01-route-scorecard-foundation.json --snapshot-date 2026-04-01 + +route-confusion-check: + $(PYTHON) tests/verify_route_confusion.py + +description-optimization: + $(PYTHON) scripts/run_description_optimization_suite.py + +judge-blind-eval: + $(PYTHON) scripts/judge_blind_eval.py --description-file SKILL.md --cases evals/blind_holdout/trigger_cases.json --semantic-config evals/semantic_config.json + +description-optimization-check: + $(PYTHON) tests/verify_description_optimization.py + +promotion-check: + $(PYTHON) tests/verify_promotion_checker.py + +python-compat-check: + $(PYTHON) tests/verify_python_compat_check.py + +architecture-maintainability-check: + $(PYTHON) tests/verify_architecture_maintainability.py + +yao-cli-check: + $(PYTHON) tests/verify_yao_cli.py + +yao-cli-world-class-check: + $(PYTHON) tests/verify_yao_cli_world_class.py + +operator-ux-check: + $(PYTHON) tests/verify_operator_ux.py + +skill-overview-check: + $(PYTHON) tests/verify_skill_overview.py + +skill-interpretation-check: + $(PYTHON) tests/verify_skill_interpretation.py + +skill-report-metrics-check: + $(PYTHON) tests/verify_skill_report_metrics.py + +skill-report-charts-check: + $(PYTHON) tests/verify_skill_report_charts.py + +html-rendering-check: + $(PYTHON) tests/verify_html_rendering.py + +skill-ir-check: + $(PYTHON) tests/verify_skill_ir.py + $(PYTHON) tests/verify_skill_ir_paths.py + +compiler-check: + $(PYTHON) tests/verify_compile_skill.py + +output-eval-check: + $(PYTHON) tests/verify_output_eval_lab.py + +output-execution-check: + $(PYTHON) tests/verify_output_execution_runs.py + +output-review-kit-check: + $(PYTHON) tests/verify_output_review_kit.py + +output-review-adjudication-check: + $(PYTHON) tests/verify_output_review_adjudication.py + +runtime-conformance-check: + $(PYTHON) tests/verify_conformance_suite.py + +runtime-permission-check: + $(PYTHON) tests/verify_runtime_permission_probes.py + +trust-check: + $(PYTHON) tests/verify_trust_check.py + +skill-atlas-check: + $(PYTHON) tests/verify_skill_atlas.py + +registry-audit-check: + $(PYTHON) tests/verify_registry_audit.py + +package-verify-check: + $(PYTHON) tests/verify_package_verification.py + +install-simulation-check: + $(PYTHON) tests/verify_install_simulation.py + +upgrade-check: + $(PYTHON) tests/verify_upgrade_check.py + +review-viewer-check: + $(PYTHON) tests/verify_review_viewer.py + +review-studio-check: + $(PYTHON) tests/verify_review_studio.py + +skill-os2-audit-check: + $(PYTHON) tests/verify_skill_os2_audit.py + +skill-os2-coverage-check: + $(PYTHON) tests/verify_skill_os2_coverage.py + +world-class-evidence-check: + $(PYTHON) tests/verify_world_class_evidence_plan.py + +world-class-ledger-check: + $(PYTHON) tests/verify_world_class_evidence_ledger.py + +world-class-intake-check: + $(PYTHON) tests/verify_world_class_evidence_intake.py + +world-class-submission-kit-check: + $(PYTHON) tests/verify_world_class_submission_kit.py + +world-class-preflight-check: + $(PYTHON) tests/verify_world_class_preflight.py + +world-class-submission-review-check: + $(PYTHON) tests/verify_world_class_submission_review.py + +world-class-runbook-check: + $(PYTHON) tests/verify_world_class_operator_runbook.py + +world-class-claim-guard-check: + $(PYTHON) tests/verify_world_class_claim_guard.py + +benchmark-reproducibility-check: + $(PYTHON) tests/verify_benchmark_reproducibility.py + +evidence-consistency-check: + $(PYTHON) tests/verify_evidence_consistency.py + +feedback-check: + $(PYTHON) tests/verify_feedback.py + +adaptation-safety-check: + $(PYTHON) tests/verify_adaptation_safety.py + +skillops-opportunity-check: + $(PYTHON) tests/verify_skillops_opportunity.py + +daily-skillops-check: + $(PYTHON) tests/verify_daily_skillops.py + +weekly-curator-check: + $(PYTHON) tests/verify_weekly_curator.py + +adoption-drift-check: + $(PYTHON) tests/verify_adoption_drift.py + +telemetry-import-check: + $(PYTHON) tests/verify_telemetry_import.py + +telemetry-emit-check: + $(PYTHON) tests/verify_telemetry_emit.py + +telemetry-hooks-check: + $(PYTHON) tests/verify_telemetry_hooks.py + +telemetry-native-host-check: + $(PYTHON) tests/verify_telemetry_native_host.py + +review-waivers-check: + $(PYTHON) tests/verify_review_waivers.py + +review-annotations-check: + $(PYTHON) tests/verify_review_annotations.py + +baseline-compare-check: + $(PYTHON) tests/verify_baseline_compare.py + +reference-scan-check: + $(PYTHON) tests/verify_reference_scan.py + +github-benchmark-scan-check: + $(PYTHON) tests/verify_github_benchmark_scan.py + +intent-confidence-check: + $(PYTHON) tests/verify_intent_confidence.py + +reference-synthesis-check: + $(PYTHON) tests/verify_reference_synthesis.py + +output-risk-profile-check: + $(PYTHON) tests/verify_output_risk_profile.py + +artifact-design-profile-check: + $(PYTHON) tests/verify_artifact_design_profile.py + +prompt-quality-profile-check: + $(PYTHON) tests/verify_prompt_quality_profile.py + +system-model-check: + $(PYTHON) tests/verify_system_model.py + +iteration-directions-check: + $(PYTHON) tests/verify_iteration_directions.py + +description-drift-history: + $(PYTHON) scripts/render_description_drift_history.py + +iteration-ledger: + $(PYTHON) scripts/render_iteration_ledger.py + +results-panel: + $(PYTHON) scripts/render_eval_dashboard.py + +regression-history: + $(PYTHON) scripts/render_regression_history.py + +context-reports: + $(PYTHON) scripts/render_context_reports.py + +portability-report: + $(PYTHON) scripts/render_portability_report.py + +portability-check: + $(PYTHON) tests/verify_portability_report.py + +failure-regression-check: + $(PYTHON) tests/verify_failure_regressions.py + +package-check: + $(PYTHON) scripts/cross_packager.py . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip + +package-failure-check: + $(PYTHON) tests/verify_packager_failures.py + +security-boundary-check: + $(PYTHON) tests/verify_security_boundaries.py + +local-install-sync-check: + $(PYTHON) tests/verify_local_install_sync.py + +snapshot-check: + $(PYTHON) tests/verify_adapter_snapshots.py + +validate: + $(PYTHON) scripts/validate_skill.py . + +lint: + $(PYTHON) scripts/lint_skill.py . + +governance-check: + $(PYTHON) scripts/governance_check.py . --require-manifest + +resource-boundary-check: + $(PYTHON) scripts/resource_boundary_check.py . + +quality-check: + $(PYTHON) tests/verify_quality_checks.py + +sync-local-install: package-check + $(PYTHON) scripts/sync_local_install.py --install-dir "$(LOCAL_SKILL_INSTALL_DIR)" + +sync-active-install: package-check + $(PYTHON) scripts/sync_local_install.py --install-dir "$(ACTIVE_SKILL_INSTALL_DIR)" + +test: python-version-check eval eval-suite route-scorecard route-confusion-check description-optimization description-optimization-check promotion-check python-compat-check architecture-maintainability-check yao-cli-check operator-ux-check skill-overview-check skill-interpretation-check skill-report-metrics-check skill-report-charts-check html-rendering-check skill-ir-check compiler-check output-eval-check output-execution-check output-review-adjudication-check runtime-conformance-check runtime-permission-check trust-check skill-atlas-check registry-audit-check package-verify-check install-simulation-check upgrade-check review-viewer-check review-studio-check skill-os2-audit-check skill-os2-coverage-check world-class-evidence-check world-class-ledger-check world-class-intake-check world-class-submission-kit-check world-class-preflight-check world-class-submission-review-check world-class-runbook-check world-class-claim-guard-check benchmark-reproducibility-check evidence-consistency-check feedback-check adaptation-safety-check skillops-opportunity-check daily-skillops-check weekly-curator-check adoption-drift-check telemetry-import-check telemetry-emit-check telemetry-hooks-check telemetry-native-host-check review-waivers-check review-annotations-check baseline-compare-check reference-scan-check github-benchmark-scan-check intent-confidence-check reference-synthesis-check output-risk-profile-check artifact-design-profile-check prompt-quality-profile-check system-model-check iteration-directions-check description-drift-history iteration-ledger regression-history context-reports portability-report portability-check failure-regression-check package-check package-failure-check security-boundary-check local-install-sync-check snapshot-check validate lint governance-check resource-boundary-check quality-check + +ci-test: python-version-check + $(PYTHON) scripts/ci_test.py + +clean: + rm -rf dist tests/tmp tests/tmp_snapshot tests/tmp_cli tests/tmp_operator_ux tests/tmp_python_compat tests/tmp_architecture_maintainability tests/tmp_skill_overview tests/tmp_skill_interpretation tests/tmp_skill_report_metrics tests/tmp_skill_report_charts tests/tmp_skill_ir tests/tmp_compile_skill tests/tmp_output_eval tests/tmp_output_execution tests/tmp_output_review_adjudication tests/tmp_conformance tests/tmp_runtime_permission tests/tmp_trust tests/tmp_skill_atlas tests/tmp_registry tests/tmp_package_verification tests/tmp_install_simulation tests/tmp_upgrade_check tests/tmp_reference_scan tests/tmp_iteration_directions tests/tmp_review_viewer tests/tmp_review_studio tests/tmp_skill_os2_audit tests/tmp_skill_os2_coverage tests/tmp_world_class_evidence tests/tmp_world_class_evidence_ledger tests/tmp_world_class_evidence_intake tests/tmp_world_class_submission_review tests/tmp_world_class_operator_runbook tests/tmp_world_class_claim_guard tests/tmp_benchmark_reproducibility tests/tmp_evidence_consistency tests/tmp_feedback tests/tmp_daily_skillops tests/tmp_weekly_curator tests/tmp_adoption_drift tests/tmp_telemetry_import tests/tmp_telemetry_emit tests/tmp_telemetry_hooks tests/tmp_telemetry_native_host tests/tmp_review_waivers tests/tmp_review_annotations tests/tmp_github_benchmark_scan tests/tmp_intent_confidence tests/tmp_reference_synthesis tests/tmp_output_risk_profile tests/tmp_artifact_design_profile tests/tmp_prompt_quality_profile tests/tmp_system_model tests/tmp_security tests/tmp_baseline_compare.json tests/tmp_baseline_compare.md + find . -type d -name __pycache__ -prune -exec rm -rf {} + diff --git a/README.md b/README.md new file mode 100644 index 0000000..708352f --- /dev/null +++ b/README.md @@ -0,0 +1,729 @@ +# Yao Meta Skill + +[![CI](https://github.com/yaojingang/yao-meta-skill/actions/workflows/test.yml/badge.svg)](https://github.com/yaojingang/yao-meta-skill/actions/workflows/test.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-black.svg)](LICENSE) +[![English](https://img.shields.io/badge/Docs-English-black)](README.md) +[![中文](https://img.shields.io/badge/Docs-%E4%B8%AD%E6%96%87-red)](docs/README.zh-CN.md) +[![日本語](https://img.shields.io/badge/Docs-%E6%97%A5%E6%9C%AC%E8%AA%9E-blue)](docs/README.ja-JP.md) +[![Français](https://img.shields.io/badge/Docs-Fran%C3%A7ais-green)](docs/README.fr-FR.md) +[![Русский](https://img.shields.io/badge/Docs-%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-purple)](docs/README.ru-RU.md) + +`YAO` stands for `Yielding AI Outcomes`: the goal is not to generate more prompt text, but to produce reusable AI assets and real operational outcomes. + +`yao-meta-skill` creates, evaluates, packages, and governs reusable agent skills. The 1.0 line focused on turning repeated workflows into installable, readable, cross-platform skill packages. The 2.0 line expands that factory into a Skill OS: a governed system for modeling a skill once, compiling it for multiple targets, testing its behavior, reviewing its release evidence, and tracking the next iteration. + +[Quick Start](#quick-start) · [Skill OS 2.0](#skill-os-20-upgrade) · [1.0 vs 2.0](#from-10-to-20) · [Operator UX](#operator-ux-commands) · [Benchmark](#weighted-quality-benchmark) · [Examples](examples/README.md) · [Evals](evals/README.md) · [Failure Library](failures/README.md) · [Method Doctrine](#method-doctrine) + +## Skill OS 2.0 Upgrade + +Skill OS 2.0 keeps the original promise of `yao-meta-skill`, but makes the package lifecycle more explicit. Instead of stopping at `SKILL.md`, it adds a semantic contract, target compilers, evaluation evidence, release gates, and operation reports around the skill. + +- **Skill IR**: a platform-neutral intermediate representation for intent, triggers, inputs, outputs, boundaries, references, and expected artifacts. +- **Target compilers and adapters**: generated surfaces for OpenAI, Claude, generic agent skills, Agent Skills compatible packages, and VS Code-oriented workflows. +- **Output Eval Lab**: trigger checks, output assertions, execution evidence, timing and token evidence, benchmark reproducibility, blind-review packs, answer keys, and adjudication reports. +- **Review Studio 2.0**: a single HTML gate page for intent, triggers, output eval, context cost, runtime checks, trust, Skill Atlas signals, adoption drift, waivers, annotations, release evidence, warnings, blockers, and fix actions. +- **Evidence and release governance**: evidence consistency checks, package verification, install simulation, runtime permission probes, world-class evidence intake, world-class ledger, operator runbook, and public claim guard. +- **SkillOps loop**: metadata-only adoption drift, telemetry hooks, adaptive proposals, daily and weekly curator reports, and portfolio-level drift signals. + +Current posture: the repository is ready for beta and external testing, while stronger public "world-class" claims remain evidence-gated. Provider-backed production evidence, human blind-review evidence, native permission execution, and real-client telemetry are tracked as separate evidence tasks instead of being treated as completed work. + +See the companion artifacts: + +- [Visual 1.0 vs 2.0 comparison report](.previews/yao-meta-skill-2-comparison/index.html) +- [Chinese desktop preview](.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2.png) +- [English desktop preview](.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2-en.png) + +## From 1.0 to 2.0 + +| Dimension | 1.0 focus | 2.0 upgrade | +| --- | --- | --- | +| Product role | Create, refactor, evaluate, and package reusable skills. | Govern the full lifecycle of a skill: creation, compilation, evaluation, review, release, telemetry, and iteration. | +| Architecture | `SKILL.md`, `agents/interface.yaml`, manifest files, and report artifacts. | Skill IR, target compilers, adapters, gate contracts, evidence ledgers, release locks, and action-oriented review pages. | +| Cross-platform delivery | OpenAI, Claude, and generic package targets. | Adds broader Agent Skills and VS Code-oriented compatibility, with registry-readable compatibility records. | +| Quality model | Trigger and structure checks plus report-based review. | Output eval, benchmark reproducibility, execution evidence, failure disclosure, blind-review packs, and evidence consistency checks. | +| Report experience | Overview HTML and first-pass review pages. | Bilingual Skill Overview v2, Review Studio 2.0, reviewer annotations, action cards, charts, and audit-oriented report contracts. | +| Release boundary | Package output with basic validation. | Package verification, install simulation, runtime permission probes, release locks, public claim guard, and operator runbooks. | +| Operating loop | Manual feedback and local iteration. | Adoption drift, metadata telemetry, SkillOps reports, adaptive proposals, and portfolio-level drift detection. | + +## 2.0 Use Cases + +- **Create a new skill from repeated work**: start with a workflow note, prompt set, transcript, runbook, or document pattern, then generate a package with a lean entrypoint, explicit inputs and outputs, references, reports, and the lightest justified gates. +- **Upgrade a personal skill into a team asset**: add interface contracts, manifests, target adapters, trust checks, output evals, reviewer waivers, release notes, and Review Studio evidence before other people depend on the skill. +- **Prepare a skill for beta release**: run package verification, install simulation, compatibility checks, runtime permission probes, and evidence consistency checks, then separate beta readiness from stronger public claims. +- **Keep a skill useful after release**: use metadata-only telemetry, adoption drift, feedback logs, SkillOps reports, and adaptive proposals to decide whether the next move should be documentation, an eval, a skill patch, or a governance update. +- **Compare with other meta-skill approaches**: keep Anthropic/OpenAI-style conversational creation and lean instruction writing where they fit, then use `yao-meta-skill` when the package needs evidence, portability, release gates, and repeatable maintenance. + +## Operator UX Commands + +These read-only helper commands turn common maintainer questions into repeatable diagnostics: + +```bash +python3 scripts/yao.py install-status --expected-source . +python3 scripts/yao.py localized-doc-sync-check +python3 scripts/yao.py pr-review-report 4 --repo yaojingang/yao-meta-skill +``` + +- `install-status` explains whether the active skill is coming from `.codex/skills`, `.agents/skills`, or the disabled mirror, and flags duplicate active installs. +- `localized-doc-sync-check` verifies that the Chinese README carries the public homepage sections that were added to the English README. +- `pr-review-report` reads GitHub PR metadata, changed files, status checks, and suggested local commands without merging or mutating the PR. + +## Capability Surface + +It turns rough workflows, transcripts, prompts, notes, and runbooks into reusable skill packages with: + +- a clear trigger surface +- a lean `SKILL.md` +- optional references, scripts, and evals +- a front-loaded intent dialogue with an intent confidence gate, so the system keeps clarifying when the true job, outputs, exclusions, or standards are still fuzzy +- a silent-by-default GitHub benchmark scan plus reference synthesis that studies top public repositories and world-class pattern tracks, then surfaces only real conflicts or uncertainty to the user +- a generated visual HTML overview for each newly initialized skill +- a Review Studio 2.0 HTML gate page that combines intent, trigger, output eval, context, runtime, trust, atlas, adoption drift, reviewer waivers, reviewer annotations, release evidence, and per-warning fix actions +- a Skill OS 2.0 audit that maps each world-class requirement to current evidence, human-required gaps, and external-required gaps +- a Skill OS 2.0 blueprint coverage report that maps the upgrade plan's core modules and recommended PRs to concrete artifacts, commands, and tests +- a world-class evidence plan that turns remaining provider, human, native-permission, and real-client telemetry gaps into executable evidence tasks +- a world-class evidence ledger that records which external and human evidence is accepted or still pending without treating planned work as proof +- a world-class evidence intake contract that validates external and human evidence packets for provenance, privacy, artifact refs, and anti-overclaim rules before ledger review +- a redacted world-class preflight report that checks local files, environment readiness, human/external prerequisites, and source blockers before operators collect evidence +- a world-class submission review queue that compares evidence packets, intake validation, source artifacts, and ledger state without accepting evidence +- a world-class operator runbook that gives reviewers the exact commands, artifacts, and collection checklist needed to close remaining evidence gaps +- a world-class claim guard that scans public claim surfaces and blocks premature completed/true claims while the evidence ledger still has pending external or human evidence +- a benchmark reproducibility manifest that checks methodology sections, required artifacts, failure disclosure, and reproduction commands +- an evidence consistency gate that compares generated reports against each other so benchmark, overview, interpretation, adoption, world-class ledger, coverage, and Review Studio facts do not drift silently +- Output Eval Lab evidence with assertion grading, execution/timing/token evidence, a blind A/B review pack, a separate answer key, and reviewer adjudication reports +- a runtime permission probe report that checks packaged target adapters for explicit permission metadata, native-enforcement flags, metadata fallback notes, and residual risks +- a Python compatibility gate that catches supported-runtime syntax hazards before they reach GitHub Actions or packaged distribution +- a side-by-side HTML review studio for first-pass human review +- an artifact design profile that defines visual direction, layout patterns, and quality gates for reports, tutorials, dashboards, screenshots, and review pages +- a prompt quality profile that abstracts need modeling, RTF mapping, complexity, and quality checks into reviewer-visible evidence instead of bloating `SKILL.md` +- a systems-thinking model that maps boundaries, feedback loops, drift risks, recurring failure patterns, and highest-leverage quality moves +- three high-value next iteration directions after the first package is created +- a lightweight feedback log that does not require a full promotion cycle +- a local-first metadata-only adoption and drift report that turns real usage signals into next iteration candidates, with optional `yao.py` CLI run capture, external client event emit hooks, hook recipes, and JSONL import that record command names and outcomes without arguments or raw content +- an explicit-source adaptive proposal loop that summarizes redacted repeated user preferences and generates approval-gated adaptation proposals without scanning private logs or writing source files +- a SkillOps opportunity scorer and decision policy that ranks redacted repeated signals, maps them to report-only, AGENTS update, existing-skill patch, or eval-addition actions, and keeps every durable write approval-gated +- a weekly SkillOps curator report that aggregates daily opportunities, Skill Atlas portfolio signals, release lock state, and world-class evidence gaps into a proposal-only maintenance queue +- a Browser/Chrome Native Messaging telemetry host that can receive length-prefixed metadata-only client events and generate a local launcher plus manifest without storing raw content +- a Skill Atlas drift layer that reads aggregate adoption reports and surfaces portfolio-level drift signals without packaging raw telemetry logs +- a baseline compare report for with-skill vs baseline review +- a conversation-style, archetype-aware quickstart that steers new packages toward scaffold, production, library, or governed fits +- Skill IR as the platform-neutral semantic contract, plus compiler reports and client-specific adapters +- Registry audit metadata with package version, owner, license, checksum, and compatibility matrix +- governance, promotion, and portability checks built into the default flow + +## Architecture + +Hero view: Skill OS 2.0 turns messy operational input into a governed, reusable skill package through a model, compile, evaluate, release, and operate loop. + +```mermaid +flowchart LR + A["Inputs
workflow / prompt / transcript / docs / notes"] --> B["Intent model
job / outputs / exclusions / standards"] + B --> C["Skill IR
trigger / contracts / resources / evidence"] + C --> D["Skill package
SKILL.md / references / scripts / reports"] + C --> E["Target compilers
OpenAI / Claude / generic / Agent Skills / VS Code"] + D --> F["Eval Lab
trigger / output / benchmark / runtime"] + E --> F + F --> G["Review Studio
gates / warnings / actions / waivers"] + G --> H["Release boundary
package verification / install simulation / claim guard"] + H --> I["SkillOps loop
feedback / adoption drift / next iteration"] + I --> B +``` + +Read it in 10 seconds: + +- **Inputs**: start from rough operational material instead of a polished spec. +- **Intent model**: make the job, outputs, exclusions, constraints, and standards explicit before generating files. +- **Skill IR**: keep the semantic contract separate from any single platform format. +- **Package and compile**: generate the lean skill package and the target-specific adapters from the same source model. +- **Evaluate and review**: turn trigger behavior, output quality, runtime checks, and trust signals into reviewable evidence. +- **Release and operate**: publish only within the current evidence boundary, then feed adoption drift and reviewer feedback into the next iteration. + +## Weighted Quality Benchmark + +This benchmark is a project-level engineering review, scored from `0-10` per dimension and weighted to `100`. GitHub stars are intentionally excluded because they measure ecosystem heat, not meta-skill engineering quality. + +The score is local engineering evidence, not a claim of world-class readiness. Public superiority claims still depend on accepted external and human evidence in the world-class ledger. + +Weighted score formula: `sum(score / 10 * weight)`. + +| Meta Skill | Method Depth 15 | Context Discipline 10 | Toolchain 15 | Eval/Test Rigor 20 | Governance 15 | Portability 10 | Onboarding/Review 5 | Local Reliability 10 | Weighted Score | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| Yao Meta Skill | 9.5 | 8.0 | 9.5 | 9.5 | 9.5 | 9.0 | 6.5 | 9.5 | 91.5 | +| Anthropic Skill Creator | 9.0 | 6.5 | 8.5 | 7.5 | 4.0 | 5.0 | 7.5 | 5.0 | 67.5 | +| OpenAI Skill Creator | 8.5 | 9.5 | 5.0 | 2.0 | 3.0 | 4.0 | 8.5 | 4.0 | 50.5 | + +| Rank | Meta Skill | Score | Core Positioning | +| ---: | --- | ---: | --- | +| 1 | Yao Meta Skill | 91.5 | A complete engineering, evaluation, governance, and portability system for reusable skills. | +| 2 | Anthropic Skill Creator | 67.5 | Strong methodology and iteration loop, with weaker local execution reliability and governance coverage. | +| 3 | OpenAI Skill Creator | 50.5 | Best treated as a concise skill-writing method guide rather than a full engineering system. | + +## Human Blind A/B Review Snapshot + +On 2026-06-29, a single human reviewer compared `yao-meta-skill` with the bundled OpenAI `skill-creator` across five realistic skill-creation scenarios: support triage, revenue reconciliation, webinar repurposing, incident postmortems, and PR review follow-up. The reviewer confirmed decisions were completed before the answer key was opened. + +Result: `yao-meta-skill` was selected in `5/5` cases. + +Evidence: + +- Review entrypoint: [reports/blind-human-review-2026-06-29/index.html](reports/blind-human-review-2026-06-29/index.html) +- Adjudication summary: [reports/blind-human-review-2026-06-29/adjudication.md](reports/blind-human-review-2026-06-29/adjudication.md) +- Recorded decisions: [reports/blind-human-review-2026-06-29/review-decisions.recorded.json](reports/blind-human-review-2026-06-29/review-decisions.recorded.json) + +Boundary: this is single-reviewer blind preference evidence. It is not provider-backed independent model execution evidence, and the per-case rationale fields are still empty. + +## Best-Fit Scenarios + +- Choose **Yao Meta Skill** when the target is a reusable team asset with explicit boundaries, trigger evaluation, governance, packaging, portability, and local execution checks. +- Choose **Anthropic Skill Creator** when the target is a conversation-first creation loop and the priority is human-guided iteration over repository-level governance. +- Choose **OpenAI Skill Creator** when the target is a compact reference for writing lean skill instructions and keeping context small. +- A practical hybrid pattern is still useful: draft conversationally, then use `yao-meta-skill` to harden the package, add evidence, and make it team-ready. + +## Quick Start + +Install the skill globally for Codex first: + +```bash +npx -y skills add yaojingang/yao-meta-skill -a codex -g -y +``` + +To install it for every supported agent, replace `-a codex` with `-a '*'`: + +```bash +npx -y skills add yaojingang/yao-meta-skill -a '*' -g -y +``` + +After installation, restart the client. Then ask for tasks such as "create a skill from this workflow", "improve this existing skill", "evaluate this skill", or "add evals to this skill" to trigger `yao-meta-skill`. + +1. Describe the workflow, prompt set, or repeated task you want to turn into a skill. +2. Start with a short, human intent dialogue so the real job, outputs, exclusions, constraints, and standards are explicit. +3. Let `quickstart` clarify intent first, then run silent benchmark scan and reference synthesis; it only surfaces explicit questions when intent is still unclear or when there is a real design conflict. +4. Use the archetype-aware `quickstart` or the full authoring flow to generate or improve the package in scaffold, production, library, or governed mode. +5. Review the generated `reports/skill-interpretation.html` first for the bilingual interpretation report. It defaults to Simplified Chinese and provides an English switch in the top right. Then open `reports/skill-overview.html` for the audit scorecard and `reports/review-studio.html` to inspect release blockers, permission approvals, and evidence paths in one page before adding more structure. + +Or use the unified authoring CLI: + +```bash +python3 scripts/yao.py quickstart --output-dir . +python3 scripts/yao.py github-benchmark-scan my-skill --query "release workflow portability" +python3 scripts/yao.py reference-scan my-skill \ + --external-reference "World Class Method::method::Borrow a tight evaluation loop.::Do not copy heavy process." \ + --user-reference "A product or repo I admire::taste::Learn the clarity and operating standard.::Do not copy wording." \ + --local-constraint "Current Library Naming::structure::Keep naming aligned with the local skill library.::Do not inherit private references." +python3 scripts/yao.py skill-interpretation my-skill +python3 scripts/yao.py review-viewer my-skill +python3 scripts/yao.py review-studio my-skill +python3 scripts/yao.py artifact-design-profile my-skill +python3 scripts/yao.py prompt-quality-profile my-skill +python3 scripts/yao.py system-model my-skill +python3 scripts/yao.py feedback my-skill --note "Tighten exclusions before adding scripts." --rating 4 --category boundary +python3 scripts/yao.py adapt-scan my-skill --source ./curated-user-signals.jsonl +python3 scripts/yao.py adapt-propose my-skill +python3 scripts/yao.py daily-skillops my-skill --source ./curated-user-signals.jsonl +python3 scripts/yao.py weekly-curator my-skill +python3 scripts/yao.py adoption-drift my-skill --record-event skill_activation --activation-type explicit --outcome accepted +YAO_CLI_TELEMETRY=1 python3 scripts/yao.py validate my-skill +python3 scripts/yao.py telemetry-emit my-skill --event skill_activation --activation-type explicit --outcome accepted --command browser-extension +python3 scripts/yao.py telemetry-hooks my-skill +python3 scripts/telemetry_native_host.py my-skill --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/ +python3 scripts/yao.py telemetry-import my-skill --input-jsonl /tmp/external-client-events.jsonl --command browser-extension +python3 scripts/yao.py review-waivers my-skill --add-waiver --gate-key trust-report --reviewer "Yao Team" --reason "Known warning accepted for this release with bounded follow-up." --expires-at 2026-09-30 +python3 scripts/yao.py review-waivers my-skill --add-waiver --gate-key permission-gates --reviewer "Yao Team" --reason "Permission warning accepted only for this non-governed release window." --expires-at 2026-09-30 +python3 scripts/yao.py review-annotations my-skill --add-annotation --gate-key output-lab --target-path reports/output_quality_scorecard.md --line 1 --body "Clarify recorded fixture vs model-executed evidence before release." +python3 scripts/yao.py baseline-compare +python3 scripts/yao.py check-update +python3 scripts/yao.py skill-ir . --output-json skill-ir/examples/yao-meta-skill.json +python3 scripts/yao.py compile-skill . --target openai --target claude --target generic --target vscode +python3 scripts/yao.py package . --platform generic --output-dir dist +python3 scripts/yao.py output-eval +python3 scripts/yao.py output-exec +python3 scripts/yao.py output-review +python3 scripts/yao.py conformance . +python3 scripts/yao.py trust . +python3 scripts/yao.py python-compat . +python3 scripts/yao.py runtime-permissions . --package-dir dist +python3 scripts/yao.py skill-atlas --workspace-root . +python3 scripts/yao.py registry-audit . +python3 scripts/yao.py package-verify . --package-dir dist --require-zip +python3 scripts/yao.py install-simulate . --package-dir dist +python3 scripts/yao.py upgrade-check . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json +python3 scripts/yao.py world-class-evidence . +SUBMISSIONS_DIR="${SUBMISSIONS_DIR:-evidence/world_class/submissions}" +python3 scripts/yao.py world-class-preflight . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-submission-kit . --output-dir "$SUBMISSIONS_DIR" +# Alternative: prefill artifact SHA-256 digests while keeping drafts template-only. +python3 scripts/yao.py world-class-submission-kit . --output-dir "$SUBMISSIONS_DIR" --prefill-artifacts +python3 scripts/yao.py world-class-intake . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-submission-review . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-ledger . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-runbook . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-claim-guard . +python3 scripts/yao.py benchmark-reproducibility . +python3 scripts/yao.py evidence-consistency . +``` + +## Local Development Source + +Development source: this repository is the source of truth for authoring and review. + +Use Python 3.11 or newer for local development. GitHub Actions runs the test suite on Python 3.11, and the Makefile checks the active interpreter before running `make test` or `make ci-test`. + +```bash +python3.11 -m venv .venv +source .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install --requirement requirements-ci.txt +make ci-test +``` + +If `python3` points to an older system interpreter, pass the interpreter explicitly: + +```bash +make PYTHON=python3.11 ci-test +``` + +Disabled mirror: `~/.agents/skills.disabled/yao-meta-skill` is the local backup mirror for this source. Keeping the mirror outside `~/.agents/skills` prevents Codex from showing a duplicate `Yao Meta Skill` while this repository is also visible in the active workspace. + +Sync the current source into the disabled mirror: + +```bash +make sync-local-install +``` + +The sync command first rebuilds the package and runs install preflight against `dist/yao-meta-skill.zip`. It refuses to sync when package extraction, adapter readability, or installer permission enforcement fails. After the preflight passes, it copies Git-tracked files plus new source files in code and guidance directories such as `scripts/`, `tests/`, `references/`, and `docs/`. It skips untracked business-skill folders and untracked private reports by default, so local experiments do not leak into the mirror. + +Restore an active global Codex install only when you intentionally want this skill discoverable outside the development workspace: + +```bash +make sync-active-install +``` + +That active install writes to `~/.agents/skills/yao-meta-skill` and can make Codex show a second `Yao Meta Skill` entry while this repository is open as a skills workspace. + +## Generated Artifact Boundaries + +Keep this repository focused on the meta-skill factory. + +- Put reusable factory examples in `examples/`. +- Put reusable benchmark evidence, regression results, and release evidence in `reports/`. +- Keep private analysis reports, customer-specific outputs, and one-off generated business skills outside this repository unless they are intentionally promoted into an example or regression fixture. +- Place real generated skills as sibling skill directories under the local skill workspace, not as top-level folders inside `yao-meta-skill`. + +## 5-Minute Workflow + +1. Start from a raw workflow note. +2. Turn it into a skill package with `SKILL.md`, `agents/interface.yaml`, and only the folders the workflow actually needs. +3. Validate the trigger description with `evals/trigger_cases.json`. +4. Export compatibility artifacts for the clients you care about. +5. Compare the result against the examples in `examples/`. + +Minimum commands: + +```bash +python3 scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json +python3 scripts/run_description_optimization_suite.py +python3 scripts/judge_blind_eval.py --description-file SKILL.md --cases evals/blind_holdout/trigger_cases.json --semantic-config evals/semantic_config.json +python3 scripts/context_sizer.py . +python3 scripts/resource_boundary_check.py . +python3 scripts/governance_check.py . --require-manifest +python3 scripts/compile_skill.py . +python3 scripts/cross_packager.py . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --zip +python3 scripts/probe_runtime_permissions.py . --package-dir dist +python3 tests/verify_packager_failures.py +``` + +Or run everything together: + +```bash +make test +``` + +Unified authoring flow: + +```bash +python3 scripts/yao.py init my-skill --description "Describe what the skill does." +python3 scripts/yao.py validate my-skill +python3 scripts/yao.py workspace-flow --target root --label first-pass +python3 scripts/yao.py review-viewer my-skill +python3 scripts/yao.py review --target root +python3 scripts/yao.py release-snapshot --target root --label release-candidate +python3 scripts/yao.py skill-ir . --output-json skill-ir/examples/yao-meta-skill.json +python3 scripts/yao.py compile-skill . +python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip +python3 scripts/yao.py runtime-permissions . --package-dir dist +python3 scripts/yao.py package-verify . --package-dir dist --require-zip +python3 scripts/yao.py test +``` + +## Results + +The homepage panel below is generated from the current eval suite so the family-level outcome is visible without opening raw JSON. + + +- regression corpus: `66` prompts across `21` families +- aggregate result: `0` false positives, `0` false negatives, average precision `1.0`, average recall `1.0` +- suite status: + +| Suite | Cases | FP | FN | Precision | Recall | +| --- | ---: | ---: | ---: | ---: | ---: | +| train | 31 | 0 | 0 | 1.0 | 1.0 | +| dev | 22 | 0 | 0 | 1.0 | 1.0 | +| holdout | 13 | 0 | 0 | 1.0 | 1.0 | + +| Family | Cases | Pass Rate | +| --- | ---: | ---: | +| `brainstorm_only` | 2 | 1.0 | +| `brainstorm_vs_build` | 1 | 1.0 | +| `complex_multi_asset` | 3 | 1.0 | +| `document_export_vs_agent_skill` | 4 | 1.0 | +| `document_only` | 3 | 1.0 | +| `explain_not_package` | 1 | 1.0 | +| `explain_only` | 5 | 1.0 | +| `future_outline_vs_build` | 4 | 1.0 | +| `iterate_existing_skill` | 5 | 1.0 | +| `long_context_document_only` | 3 | 1.0 | +| `long_context_near_neighbor` | 3 | 1.0 | +| `long_context_summary_only` | 2 | 1.0 | +| `long_context_trigger` | 4 | 1.0 | +| `meta_skill_creation` | 1 | 1.0 | +| `one_off_vs_reusable` | 2 | 1.0 | +| `package_for_team` | 2 | 1.0 | +| `paraphrase_trigger` | 5 | 1.0 | +| `partial_scaffold_not_full_skill` | 4 | 1.0 | +| `summary_only` | 3 | 1.0 | +| `translate_only` | 4 | 1.0 | +| `workflow_to_skill` | 5 | 1.0 | + +Full reports: [reports/eval_suite.json](reports/eval_suite.json) and [reports/family_summary.md](reports/family_summary.md) + + +- packaging validation: `openai`, `claude`, `generic`, and `vscode` targets pass contract checks and carry IR provenance, semantic parity metadata, and target-native behavior contracts +- target compiler validation: `openai`, `claude`, `generic`, Agent Skills compatible, and VS Code / Copilot contracts are compiled from Skill IR with generated-file mappings, adapter modes, native surfaces, permission enforcement notes, and unsupported-feature notes +- runtime permission probes: `openai`, `claude`, `generic`, and `vscode` adapters expose explicit permission contracts; current targets report `0` native-enforcement adapters and `4` metadata fallbacks with residual risks visible to reviewers +- portability score: `100/100` with neutral activation, execution, trust, and degradation metadata preserved across all exported targets +- description optimization suite: root, team frontend review, and governed incident command pass blind and adversarial holdout gates; governed incident command still carries one visible holdout miss, and adversarial calibration plus family drift are now tracked separately +- judge-backed blind eval: root, team frontend review, and governed incident command now pass an independent rubric judge on blind holdout prompts +- human blind A/B snapshot: a single reviewer selected `yao-meta-skill` over the bundled OpenAI `skill-creator` in `5/5` realistic skill-creation scenarios; evidence is published in [reports/blind-human-review-2026-06-29/adjudication.md](reports/blind-human-review-2026-06-29/adjudication.md) +- packaging failure fixtures: invalid metadata, invalid YAML, and unsupported targets fail as expected +- failure library regressions: anti-pattern families pass automated checks +- governance and resource-boundary checks are part of the default test path +- root governance maturity score: `90/100`; governed benchmark example: `95/100` +- CJK-aware trigger matching is now covered by explicit Chinese build, packaging, eval, and near-neighbor cases +- context budgets: root `944/1000`, complex benchmark `790/1000`, governed benchmark `760/1000` +- quality density: root `137.7`, complex benchmark `164.6`, governed benchmark `171.1` +- regression milestones are tracked in [reports/regression_history.md](reports/regression_history.md) +- description drift history is tracked in [reports/description_drift_history.md](reports/description_drift_history.md) +- route confusion is tracked in [reports/route_scorecard.md](reports/route_scorecard.md) +- promotion evidence is summarized in [reports/iteration_ledger.md](reports/iteration_ledger.md) +- promotion decisions are published in [reports/promotion_decisions.md](reports/promotion_decisions.md) +- candidate lifecycle states are published in [reports/candidate_registry.md](reports/candidate_registry.md) +- lightweight with-skill vs baseline comparison is published in [reports/baseline-compare.md](reports/baseline-compare.md) +- Review Studio 2.0 gate evidence is published in [reports/review-studio.html](reports/review-studio.html) +- Review Studio fix actions are embedded in [reports/review-studio.json](reports/review-studio.json) +- Skill OS 2.0 blueprint coverage is published in [reports/skill_os2_coverage.md](reports/skill_os2_coverage.md) +- reviewer waiver evidence is published in [reports/review_waivers.md](reports/review_waivers.md) +- remaining world-class evidence tasks are published in [reports/world_class_evidence_plan.md](reports/world_class_evidence_plan.md) +- current world-class evidence acceptance state is published in [reports/world_class_evidence_ledger.md](reports/world_class_evidence_ledger.md) +- world-class evidence intake readiness is published in [reports/world_class_evidence_intake.md](reports/world_class_evidence_intake.md) +- world-class submission review queue is published in [reports/world_class_submission_review.md](reports/world_class_submission_review.md) +- world-class operator runbook is published in [reports/world_class_operator_runbook.md](reports/world_class_operator_runbook.md) and [reports/world_class_operator_runbook.html](reports/world_class_operator_runbook.html) +- world-class public claim guard status is published in [reports/world_class_claim_guard.md](reports/world_class_claim_guard.md) +- benchmark reproducibility evidence is published in [reports/benchmark_reproducibility.md](reports/benchmark_reproducibility.md) +- cross-report evidence consistency is published in [reports/evidence_consistency.md](reports/evidence_consistency.md) +- target compiler evidence is published in [reports/compiled_targets.md](reports/compiled_targets.md) +- Python runtime compatibility evidence is published in [reports/python_compatibility.md](reports/python_compatibility.md) +- registry package metadata and audit status are published in [reports/registry_audit.md](reports/registry_audit.md) +- package archive verification is published in [reports/package_verification.md](reports/package_verification.md) +- temporary local install simulation is published in [reports/install_simulation.md](reports/install_simulation.md) +- upgrade diff, version-bump recommendation, and release-note evidence are published in [reports/upgrade_check.md](reports/upgrade_check.md) +- local-first adoption and drift telemetry is summarized in [reports/adoption_drift_report.md](reports/adoption_drift_report.md) +- context budget summaries are tracked in [reports/context_budget.md](reports/context_budget.md) +- portability status is tracked in [reports/portability_score.md](reports/portability_score.md) + +## Current Strengths + +The latest weighted review puts Yao at `91.5/100`. The strongest dimensions are the ones that matter most when skills become long-lived team assets: + +- **Method depth `9.5`**: formal skill engineering doctrine, archetypes, gate selection, non-skill decisions, lifecycle governance, and resource boundaries. +- **Toolchain completeness `9.5`**: authoring, validation, benchmark scan, description optimization, report generation, promotion checks, packaging, CI, and portability checks are wired into one operational flow. +- **Eval and test rigor `9.5`**: trigger quality is checked with train/dev/holdout, blind holdout, adversarial holdout, judge-backed blind eval, route confusion, drift history, and promotion gates. +- **Governance and lifecycle `9.5`**: important skills can carry owner, lifecycle state, review cadence, maturity score, trust boundaries, promotion decisions, and regression history. +- **Local execution reliability `9.5`**: the repository is executable locally through `make test`, `make ci-test`, and the unified `scripts/yao.py` authoring CLI. +- **Portability and distribution `9.0`**: neutral source metadata, client adapters, degradation rules, packaging contracts, and portability scoring preserve reusable semantics across target environments. +- **Systems stability**: generated skills now include a system model that turns boundary discipline, feedback loops, drift watch, and leverage-point analysis into reviewer-visible evidence. +- **Context discipline `8.0`**: the entrypoint is still held under budget, but this is tracked as a live constraint because the system now carries more reports, examples, benchmark assets, and generated evidence. +- **Onboarding and review experience `6.5`**: quickstart, HTML overview, side-by-side review viewer, and feedback logs have improved the first-run experience, but this remains the clearest UX improvement area. + +The current direction is deliberate: keep the entrypoint light, make evaluation hard to fake, make governance visible, and continue reducing the friction of first-time creation and review. + +## Why Yao + +- **Lightweight**: the entrypoint stays compact, context budgets are explicit, and extra structure is added only when it pays for itself. +- **Rigorous**: trigger quality is checked with family regressions, blind holdout, adversarial holdout, route confusion, judge-backed blind eval, and promotion gates. +- **Governed**: important skills are treated as maintainable assets with lifecycle state, maturity expectations, ownership, and review cadence. +- **Portable**: source metadata stays neutral while adapters, degradation rules, and packaging contracts preserve reusable semantics across environments. + +## What It Does + +This project helps you create, refactor, evaluate, and package skills as durable capability bundles rather than one-off prompts. + +The design logic is simple: + +1. Capture the real recurring job behind the user's request. +2. Set a clean skill boundary so one package does one coherent job. +3. Optimize the trigger description before over-writing the body. +4. Keep the main skill file small and move details into references or scripts. +5. Add quality gates only when they pay for themselves. +6. Export compatibility artifacts only for the clients you actually need. + +## Method Doctrine + +The repository now treats method as a first-class asset instead of scattered guidance. + +- [Skill Engineering Method](references/skill-engineering-method.md) +- [Intent Dialogue](references/intent-dialogue.md) +- [Reference Scan Strategy](references/reference-scan.md) +- [Pattern Extraction Doctrine](references/pattern-extraction-doctrine.md) +- [Output Quality Risk](references/output-quality-risk.md) +- [Authoring Discipline](references/authoring-discipline.md) +- [Skill Archetypes](references/skill-archetypes.md) +- [Gate Selection](references/gate-selection.md) +- [Iteration Philosophy](references/iteration-philosophy.md) +- [Non-Skill Decision Tree](references/non-skill-decision-tree.md) +- [Regression Cause Taxonomy](references/regression-cause-taxonomy.md) +- [Human Review Template](references/human-review-template.md) + +## Why It Exists + +Most teams keep valuable operating knowledge scattered across chats, personal prompts, oral habits, and undocumented workflows. This project converts that hidden process knowledge into: + +- discoverable skill packages +- repeatable execution flows +- lower-context instructions +- reusable team assets +- compatibility-ready distributions + +## Repository Structure + +```text +yao-meta-skill/ +├── SKILL.md +├── README.md +├── VERSION +├── LICENSE +├── .gitignore +├── agents/ +│ └── interface.yaml +├── evals/ +├── examples/ +├── references/ +├── scripts/ +└── templates/ +``` + +## Core Components + +### `SKILL.md` + +The main skill entrypoint. It defines the trigger surface, operating modes, compact workflow, and output contract. + +### `agents/interface.yaml` + +The neutral metadata source of truth. It stores display and compatibility metadata without locking the source tree to one vendor-specific path. + +### `references/` + +Long-form material that should not bloat the main skill file. This includes design rules, evaluation guidance, compatibility strategy, and quality rubrics. + +### `scripts/` + +Utility scripts that make the meta-skill operational: + +- `trigger_eval.py`: evaluates trigger descriptions with semantic intent concepts, explicit exclusions, and near-neighbor prompts +- `run_eval_suite.py`: runs train/dev/holdout trigger suites, reports family-level regressions, and fails if aggregate regressions appear +- `optimize_description.py`: generates candidate descriptions, scores them on dev, visible holdout, blind holdout, and adversarial holdout suites, then reports calibration and family health +- `judge_blind_eval.py`: applies an independent rubric judge to blind-holdout prompts so blind acceptance is not backed only by the main threshold scorer +- `run_description_optimization_suite.py`: runs description optimization across the root skill and governed examples, then writes reusable reports and optional drift snapshots with calibration and family summaries +- `promotion_checker.py`: applies promotion policy to current description candidates, writes promotion decisions, builds candidate registries, and emits iteration bundles with review stubs +- `create_iteration_snapshot.py`: freezes the current promotion decision into a versioned release snapshot with review, route, and context evidence +- `yao.py`: unified authoring CLI that exposes init, validate, optimize-description, promote-check, python-compat, review, release-snapshot, workspace-flow, report, skill-report, skill-interpretation, skill-ir, compile-skill, output-exec, output-review, skill-os2-audit, skill-os2-coverage, world-class-evidence, world-class-ledger, world-class-intake, world-class-preflight, world-class-submission-kit, world-class-submission-review, world-class-runbook, world-class-claim-guard, benchmark-reproducibility, evidence-consistency, adapt-scan, adapt-propose, adapt-apply, daily-skillops, weekly-curator, telemetry-emit, telemetry-hooks, telemetry-import, package, registry-audit, package-verify, install-simulate, upgrade-check, review-waivers, and test as one entrypoint +- `render_description_drift_history.py`: turns description-optimization snapshots into a readable drift-history report +- `build_confusion_matrix.py`: scores route confusion across tracked sibling skills and `no_route` cases, then writes a route scorecard and optional milestone snapshot +- `render_iteration_ledger.py`: compresses regression milestones, description optimization drift, and route scorecards into one iteration-facing ledger +- `context_sizer.py`: estimates context weight and warns when the initial load gets too large +- `resource_boundary_check.py`: audits whether detail is split across `SKILL.md`, `references/`, `scripts/`, `assets/`, and `evals/` appropriately +- `governance_check.py`: validates owner, review cadence, lifecycle stage, and maturity metadata +- `render_context_reports.py`: generates root and example context-budget reports plus a shared context summary +- `render_regression_history.py`: turns milestone snapshots into a readable regression history report +- `render_skill_os2_audit.py`: renders a requirement-by-requirement Skill OS 2.0 audit that separates landed local evidence from human-required and external-required gaps +- `render_skill_os2_coverage.py`: maps the Skill OS 2.0 upgrade blueprint to local artifacts, commands, tests, and remaining evidence boundaries +- `render_daily_skillops_report.py`: renders an explicit-source Daily SkillOps operations report that summarizes redacted user patterns, proposal-only adaptations, approval state, release evidence, and world-class evidence gaps without scanning private logs or applying patches +- `render_weekly_curator_report.py`: renders a weekly SkillOps curator report from generated daily reports, Skill Atlas, benchmark lock, evidence consistency, and world-class ledger state without scanning private logs or applying patches +- `skillops_opportunity.py`: scores redacted SkillOps opportunities and maps them to approval-gated action types such as report-only, AGENTS update, existing-skill patch, or eval addition +- `render_world_class_evidence_plan.py`: renders executable evidence tasks for remaining world-class gaps without treating planned external work as completed evidence +- `render_world_class_evidence_ledger.py`: renders a machine-checkable ledger for current world-class evidence acceptance, anti-overclaim guards, provenance requirements, and privacy contracts +- `render_world_class_evidence_intake.py`: validates world-class external and human evidence packets against provenance, privacy, artifact, and anti-overclaim requirements before ledger review +- `render_world_class_preflight.py`: renders redacted collection preflight checks for pending provider, human, native-permission, and native-client evidence without accepting evidence +- `render_world_class_submission_review.py`: renders a read-only queue that compares submissions, intake validation, source evidence, and ledger state without accepting evidence +- `render_world_class_operator_runbook.py`: renders an operator-facing checklist and command map for collecting pending world-class evidence without accepting evidence +- `render_world_class_claim_guard.py`: scans README, docs, and reports for premature world-class completion claims while accepted evidence is still pending +- `render_benchmark_reproducibility.py`: renders methodology, artifact, failure-disclosure, and reproduction-command evidence for public benchmark claims +- `render_evidence_consistency.py`: compares generated report facts across benchmark reproducibility, overview, interpretation, adoption drift, world-class ledger, coverage, and Review Studio artifacts +- `python_compat_check.py`: checks Python source for supported-runtime compatibility hazards such as Python 3.11 f-string expression backslashes +- `cross_packager.py`: builds client-specific export artifacts from Skill IR plus neutral metadata, with explicit platform contracts and validation +- `render_portability_report.py`: scores cross-environment portability from neutral metadata, degradation rules, and consumer validation coverage +- `render_skill_overview.py`: generates the white-background bilingual HTML skill audit report with sticky four-character Chinese navigation, top-right language switch, v2 scorecard, inline SVG charts, contract boundary, quality review, risk governance, assets, and iteration roadmap +- `render_skill_interpretation.py`: renders `reports/skill-interpretation.html/json` as the first-class post-creation interpretation report while reusing the Skill Overview v2 model and Kami white layout +- `export_skill_ir.py`: exports the 2.0 platform-neutral Skill IR contract from `SKILL.md`, manifest, interface metadata, evals, resources, and reports +- `compile_skill.py`: compiles Skill IR into target-specific semantic contracts, generated-file maps, adapter modes, target-native behavior contracts, preserved semantics, warnings, and unsupported-feature notes +- `run_output_eval.py`: runs the Output Eval Lab v0 with static with-skill vs baseline assertion grading, blind A/B review pack generation, and separate answer key artifacts +- `run_output_execution.py`: records output-eval execution evidence, distinguishing recorded fixtures, command runners, and provider-backed model runs with timing and token metadata +- `local_output_eval_runner.py`: deterministic local runner for command-executed output-eval smoke evidence without claiming provider-backed model generation +- `adjudicate_output_review.py`: records reviewer choices for blind A/B output evals, compares them with the answer key, and renders pending, match, disagreement, and invalid-decision audit reports +- `render_review_annotations.py`: records reviewer annotations tied to Review Studio gates, source/report paths, and optional line numbers, with open blocker annotations reflected in Review Studio decisions +- `run_conformance_suite.py`: verifies runtime conformance for OpenAI, Claude, Agent Skills, VS Code/Copilot-style, and generic targets +- `trust_check.py`: generates the trust/security report for scripts, dependencies, secret risk, bounded network host policy, execution-level `--help` smoke checks, permission inputs, trust metadata, and stable source-contract integrity +- `build_skill_atlas.py`: builds the Skill Atlas catalog, route-overlap matrix, dependency graph, stale report, owner gaps, aggregate drift signals, and HTML overview for a multi-skill workspace +- `registry_audit.py`: builds registry package metadata and audits version, owner, license, checksum, Skill IR source, and compatibility matrix +- `verify_package.py`: verifies generated package manifests, target adapters, zip archive safety, archive checksum, and registry parity +- `simulate_install.py`: extracts a generated zip into a temporary skill root and verifies entrypoint, manifest, interface, reports, and adapters can be loaded +- `upgrade_check.py`: compares current and previous registry package metadata, recommends a version bump, and blocks incompatible upgrade claims +- `render_adoption_drift_report.py`: records metadata-only local telemetry and renders adoption, missed-trigger, bad-output, script-error, and review-drift signals without packaging raw event logs +- `import_telemetry_events.py`: imports external metadata-only telemetry JSONL after whole-file privacy validation, then refreshes the aggregate adoption drift report +- `emit_telemetry_event.py`: emits one metadata-only external client event into a local spool for later `telemetry-import`, with dry-run validation and raw-content field blocking +- `render_telemetry_hook_recipes.py`: renders Browser, Chrome, VS Code, CLI wrapper, and provider-adapter telemetry hook recipes with dry-run commands and explicit native-integration caveats +- `telemetry_native_host.py`: receives Browser/Chrome Native Messaging length-prefixed JSON events, rejects raw-content fields, appends metadata-only events, and writes local launcher/manifest files for operator installation +- `yao_cli_telemetry.py`: opt-in metadata-only `yao.py` run capture for command name, source, outcome, and failure class without command arguments or raw content +- `render_review_waivers.py`: validates human reviewer risk approvals with gate keys, reasons, expiry dates, and blocker-safe waiver policy +- `init_skill.py`, `lint_skill.py`, `validate_skill.py`, `diff_eval.py`: minimal authoring toolchain +- `check_update.py`: checks GitHub for a newer `VERSION` or remote manifest version and reports a reinstall hint without modifying local files +- `render_output_risk_profile.py`: predicts output-specific failure modes such as generic headings, citation clutter, screenshot mistakes, weak Markdown tables, and missing execution assumptions + +### `evals/` + +Reusable trigger and packaging checks, including baseline and improved descriptions for comparison plus the root semantic configuration that drives description optimization. + +This directory also contains route confusion fixtures and promotion policy rules for deciding when a route is promotable. + +### `examples/` + +End-to-end examples showing raw workflow input, design summary, final generated skill shape, and targeted description-optimization packs where route wording is tuned against example-specific dev and holdout cases. + +### `.github/workflows/test.yml` + +Continuous integration entrypoint that runs the full local regression suite on push and pull request. + +## Validation Notes + +- Trigger evaluation now uses a local semantic-intent model with explicit positive concepts, exclusion concepts, and boundary-case reporting. +- The sample trigger report now covers a larger positive, negative, and near-neighbor set rather than a tiny demo set. +- Train/dev/holdout trigger suites now separate iterative tuning from final verification. +- Description optimization now uses dev for ranking, visible holdout for non-regression, blind holdout for acceptance, and adversarial holdout for harder route-collision checks without feeding the ranking loop. +- Judge-backed blind eval now adds a rubric-based second opinion for blind prompts, so blind acceptance is not decided by one scorer alone. +- Description drift history now records adversarial calibration gaps and family coverage, so routing changes can be judged on confidence and family stability rather than raw error counts alone. +- Route confusion is now tracked explicitly across the root meta-skill, frontend review skill, governed incident skill, and `no_route` cases, so route theft is visible instead of implicit. +- Promotion policy now requires visible holdout, blind holdout, adversarial holdout, and route confusion to stay clean before a description should be considered promotable. +- Promotion checking now emits explicit decisions, candidate lifecycle states, iteration bundles, and human-review stubs rather than leaving promotion as a prose-only step. +- Promotion decisions now distinguish “no candidate beat current” from “current still has residual route risk,” so iteration can be audited without forcing every issue into a false block. +- Packaging validation now uses explicit contracts and YAML parsing, but it is still a lightweight local validation layer rather than a full platform integration suite. +- `evals/failure-cases.md` captures known weak spots that should remain part of regression checks. +- `failures/` captures reusable anti-pattern writeups and machine-runnable failure cases for routing, packaging, and authoring failures. +- `tests/verify_packager_failures.py` checks that invalid metadata, invalid YAML, and unsupported targets fail clearly. +- Governance metadata and resource-boundary rules now have runnable checks instead of staying as prose only. +- Governance checks now emit a maturity score so governed assets can be compared instead of only pass/fail checked. +- Description optimization drift history is now versioned separately from the main trigger regression history so routing improvements are visible over time. +- Iteration evidence now records why a candidate was kept, blocked, or promotable via a shared regression-cause taxonomy and bundle artifacts. +- Declared maturity tiers are checked against recommended minimum governance scores, so `production`, `library`, and `governed` assets can be compared without forcing every strong example into the same label. +- Context budgets are now tiered and explicit, so a governed skill can still choose a stricter `production`-sized initial-load budget. +- Resource-boundary checks now detect decorative directories and compute a local quality-density signal instead of only checking raw token counts. + +### `templates/` + +Starter templates for simple and more advanced skill packages. + +## How To Use + +### 1. Use the skill directly + +Invoke `yao-meta-skill` when you want to: + +- create a new skill +- improve an existing skill +- add evals to a skill +- convert a workflow into a reusable package +- prepare a skill for wider team adoption + +### 2. Generate a new skill package + +The typical flow is: + +1. describe the workflow or capability +2. identify trigger phrases and outputs +3. choose scaffold, production, or library mode +4. generate the package +5. run the sizing and trigger checks if needed +6. export target-specific compatibility artifacts from the Skill IR contract + +### 3. Export compatibility artifacts + +Examples: + +```bash +python3 scripts/export_skill_ir.py ./yao-meta-skill --output-json ./yao-meta-skill/reports/skill-ir.json +python3 scripts/compile_skill.py ./yao-meta-skill --target openai --target claude --target generic +python3 scripts/cross_packager.py ./yao-meta-skill --platform openai --platform claude --expectations evals/packaging_expectations.json --zip +python3 scripts/context_sizer.py ./yao-meta-skill +python3 scripts/resource_boundary_check.py ./yao-meta-skill +python3 scripts/governance_check.py ./yao-meta-skill --require-manifest +python3 scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json --baseline-description-file evals/baseline_description.txt +``` + +## Advantages + +- **Method-first, not prompt-first**: skill creation is treated as a formal engineering workflow with archetypes, gate selection, and non-skill decisions. +- **Trigger-aware by design**: descriptions are optimized with route confusion, blind holdout, adversarial families, and promotion policy instead of one-shot intuition. +- **Lightweight at the entrypoint**: `SKILL.md` stays compact while references, scripts, and evals are only added when they pay for themselves. +- **Toolchain-backed**: initialization, validation, optimization, reporting, packaging, and testing are available through one unified CLI and CI path. +- **Governed as an asset**: important skills can carry ownership, lifecycle state, maturity expectations, and review cadence. +- **Portable by default**: source metadata stays neutral while adapters and degradation rules preserve compatibility across target environments. +- **Evidence-rich**: route scorecards, regression history, context budgets, portability scores, and promotion decisions are published as artifacts instead of hidden implementation detail. + +## Best Fit + +This project is best for: + +- agent builders +- internal tooling teams +- prompt engineers moving toward structured skills +- organizations building reusable skill libraries + +## Documentation + +| Language | Entry | +| --- | --- | +| English | [README.md](README.md) | +| 中文 | [docs/README.zh-CN.md](docs/README.zh-CN.md) | +| 日本語 | [docs/README.ja-JP.md](docs/README.ja-JP.md) | +| Français | [docs/README.fr-FR.md](docs/README.fr-FR.md) | +| Русский | [docs/README.ru-RU.md](docs/README.ru-RU.md) | + +## Examples And Evals + +- Examples: [examples/README.md](examples/README.md) +- Evals: [evals/README.md](evals/README.md) +- Failure library: [failures/README.md](failures/README.md) +- Failure regression check: [verify_failure_regressions.py](tests/verify_failure_regressions.py) +- Regression history: [reports/regression_history.md](reports/regression_history.md) +- Root governance score: [reports/governance_score.json](reports/governance_score.json) +- Packaging contracts: [references/packaging-contracts.md](references/packaging-contracts.md) +- Governance model: [references/governance.md](references/governance.md) +- Resource boundary spec: [references/resource-boundaries.md](references/resource-boundaries.md) +- Platform capability matrix: [references/platform-capability-matrix.md](references/platform-capability-matrix.md) +- Failure fixtures: [tests/fixtures](tests/fixtures) +- Adapter snapshots: [tests/snapshots](tests/snapshots) +- Evolution example: [examples/evolution-frontend-review/README.md](examples/evolution-frontend-review/README.md) +- Governed example: [examples/governed-incident-command/design-summary.md](examples/governed-incident-command/design-summary.md) +- Governed example score: [examples/governed-incident-command/generated-skill/reports/governance_score.json](examples/governed-incident-command/generated-skill/reports/governance_score.json) + +## License + +MIT. See [LICENSE](LICENSE). diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..4af6e18 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`frostbyte_neo/yao-meta-skill` +- 原始仓库:https://gitea-dev.autobee.pro/frostbyte_neo/yao-meta-skill +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..a2c7a6e --- /dev/null +++ b/SKILL.md @@ -0,0 +1,53 @@ +--- +name: yao-meta-skill +description: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. +metadata: + author: Yao Team +--- + +# Yao Meta Skill + +## Router Rules + +- Route by frontmatter `description`. +- Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`. +- Use the lightest reliable process. + +## Modes + +- `Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical. +- Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md). + +## Compact Workflow + +1. One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`. +2. Capture job, output, exclusions, constraints, standards, lightest fit. +3. Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict. +4. Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders. +5. Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned. + +Playbooks: [Method](references/skill-engineering-method.md), [Intent](references/intent-dialogue.md), [Skill IR](references/skill-ir-method.md), [Output Eval](references/output-eval-method.md), [Review Studio](references/review-studio-method.md). + +## Skill OS 2.0 Gates + +For production/library/governed/team releases, run Skill IR, compiler, trigger/output eval, Skill Atlas, conformance, trust, registry/package/install, upgrade, drift, waiver, Review Studio. + +## Governed Package Boundary + +For file-backed/release-critical/governed packages, name `input_files` as `file-backed fixture`; include `owner`, `review cadence`, `input_files`, `output contract`, `rollback boundary`; require `trust report` and `reports/output_quality_scorecard.md`; mark unavailable telemetry/approvals/metrics/benchmarks as `missing evidence`; do not fabricate evidence. + +Preserve labels literally when they apply: `file-backed fixture`, `input_files`, `output contract`, `rollback boundary`, `trust report`, `reports/output_quality_scorecard.md`, `missing evidence`. + +## First-Turn Style + +- Start from the user's work/outcome before structure. +- Ask only `2-3` key questions unless enough detail exists. +- In Chinese, sound soft and companion-like; use [Intent Dialogue](references/intent-dialogue.md). + +## Output Contract + +Create/refactor/package: produce `SKILL.md`, aligned `agents/interface.yaml`, justified assets, boundary/exclusion/gate summary. Audit/evaluate-only: findings + proposed fixes; edit files only if asked. No-skill: no files. + +## Reference Map + +Primary: [Method](references/skill-engineering-method.md), [Artifact Design](references/artifact-design-doctrine.md), [Systems](references/systems-thinking-doctrine.md), [Governance](references/governance.md), [SkillOps](references/skillops-decision-policy.md). diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..9084fa2 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.1.0 diff --git a/agents/interface.yaml b/agents/interface.yaml new file mode 100644 index 0000000..35912db --- /dev/null +++ b/agents/interface.yaml @@ -0,0 +1,26 @@ +interface: + display_name: "Yao Meta Skill" + short_description: "Create trigger-aware agent skills" + default_prompt: "Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals." +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "openai" + - "claude" + - "generic" + - "vscode" + activation: + mode: "manual" + paths: [] + execution: + context: "inline" + shell: "bash" + trust: + source_tier: "local" + remote_inline_execution: "forbid" + remote_metadata_policy: "allow-metadata-only" + degradation: + openai: "metadata-adapter" + claude: "neutral-source-plus-adapter" + generic: "neutral-source" + vscode: "agent-skills-source-with-vscode-notes" diff --git a/assets/review-studio.css b/assets/review-studio.css new file mode 100644 index 0000000..20bdd87 --- /dev/null +++ b/assets/review-studio.css @@ -0,0 +1,749 @@ +:root { + --ink: #1B365D; + --text: #24201d; + --muted: #746d66; + --line: #e7ded2; + --soft: #faf8f5; + --pass: #1e6b52; + --warn: #9a6718; + --block: #9b2c2c; + } + * { box-sizing: border-box; } + html { scroll-behavior: smooth; } + body { + margin: 0; + background: #ffffff; + color: var(--text); + font-family: Georgia, "Times New Roman", "Songti SC", serif; + line-height: 1.58; + } + nav { + position: sticky; + top: 0; + z-index: 10; + display: flex; + gap: 4px; + justify-content: center; + flex-wrap: wrap; + padding: 10px 16px; + background: rgba(255,255,255,0.94); + border-bottom: 1px solid var(--line); + backdrop-filter: blur(10px); + } + nav a { + color: var(--ink); + text-decoration: none; + font-size: 14px; + padding: 7px 10px; + border-radius: 6px; + } + nav a:hover { background: var(--soft); } + main { max-width: 1180px; margin: 0 auto; padding: 44px 28px 76px; } + header { border-bottom: 1px solid var(--line); padding-bottom: 28px; margin-bottom: 28px; } + .eyebrow { color: var(--ink); font-size: 14px; letter-spacing: .08em; text-transform: uppercase; } + h1, h2, h3 { color: var(--text); font-weight: 500; margin: 0; letter-spacing: 0; } + h1 { font-size: clamp(34px, 5vw, 64px); line-height: 1.03; max-width: 920px; margin-top: 12px; } + h2 { font-size: 30px; margin-bottom: 14px; } + h3 { font-size: 19px; } + p { margin: 0; } + .lede { max-width: 820px; color: var(--muted); font-size: 20px; margin-top: 18px; } + .decision { + display: inline-flex; + align-items: baseline; + gap: 12px; + margin-top: 24px; + padding: 12px 16px; + border: 1px solid var(--line); + border-radius: 8px; + color: var(--ink); + background: var(--soft); + } + .decision strong { font-size: 28px; } + section { padding: 30px 0; border-bottom: 1px solid var(--line); scroll-margin-top: 76px; } + .metrics, .gates { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 14px; + } + .gates { grid-template-columns: repeat(4, minmax(0, 1fr)); } + .metric, .gate { + border: 1px solid var(--line); + border-radius: 8px; + padding: 16px; + background: #fff; + min-width: 0; + } + .metric span, .gate span { display: block; color: var(--muted); font-size: 13px; } + .metric strong { display: block; color: var(--ink); font-size: 34px; line-height: 1.1; margin: 8px 0; } + .metric p, .gate p, .gate footer, .issues span, .evidence span { color: var(--muted); font-size: 14px; overflow-wrap: anywhere; } + .gate { display: flex; flex-direction: column; gap: 10px; } + .gate.pass { border-top: 4px solid var(--pass); } + .gate.warn { border-top: 4px solid var(--warn); } + .gate.block { border-top: 4px solid var(--block); } + .gate footer { border-top: 1px solid var(--line); padding-top: 10px; } + a { color: var(--ink); } + .twocol { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 22px; + align-items: start; + } + .panel { + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; + background: #fff; + } + .panel p { color: var(--muted); } + .kv-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px 12px; + margin: 2px 0 0; + } + .kv-grid div { + min-width: 0; + padding: 9px 0 0; + border-top: 1px solid var(--line); + } + .kv-grid dt { + color: var(--muted); + font-size: 13px; + } + .kv-grid dd { + margin: 2px 0 0; + color: var(--ink); + font-size: 15px; + overflow-wrap: anywhere; + } + .issues, .evidence { + list-style: none; + padding: 0; + margin: 0; + display: grid; + gap: 12px; + } + .issues li, .evidence li { + border-left: 3px solid var(--line); + padding-left: 12px; + display: grid; + gap: 3px; + } + .muted { color: var(--muted); } + .actions-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + } + .annotations-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + } + .action-card { + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; + background: #fff; + display: grid; + gap: 10px; + min-width: 0; + } + .action-card.warn { border-left: 4px solid var(--warn); } + .action-card.block { border-left: 4px solid var(--block); } + .action-card.with-evidence { + grid-column: 1 / -1; + } + .action-evidence-panel { + display: grid; + gap: 12px; + border-top: 1px solid var(--line); + padding-top: 12px; + margin-top: 2px; + } + .action-evidence-panel h4 { + margin: 0; + color: var(--ink); + font-size: 16px; + font-weight: 500; + } + .action-evidence-panel p { + color: var(--muted); + font-size: 14px; + } + .action-evidence-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; + } + .action-evidence-item { + border: 1px solid var(--line); + border-radius: 8px; + padding: 14px; + display: grid; + gap: 10px; + min-width: 0; + background: var(--soft); + } + .action-evidence-item.pending { border-left: 4px solid var(--warn); } + .action-evidence-item.accepted { border-left: 4px solid var(--pass); } + .action-evidence-item h4, + .action-evidence-item h5 { + margin: 0; + color: var(--ink); + font-weight: 500; + } + .action-evidence-item h4 { font-size: 16px; } + .action-evidence-item h5 { font-size: 14px; } + .action-evidence-item section { + border-top: 1px solid var(--line); + padding-top: 10px; + display: grid; + gap: 8px; + } + .action-command-details, + .action-phase-details, + .action-runbook-details, + .action-collection-details { + border-top: 1px solid var(--line); + padding-top: 10px; + display: grid; + gap: 8px; + } + .action-command-details summary, + .action-phase-details summary, + .action-runbook-details summary, + .action-collection-details summary { + cursor: pointer; + color: var(--ink); + font-size: 14px; + font-weight: 500; + } + .action-command-details[open] summary, + .action-phase-details[open] summary, + .action-runbook-details[open] summary, + .action-collection-details[open] summary { + margin-bottom: 8px; + } + .action-collection-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + } + .action-collection-grid section { + border-top: 1px solid var(--line); + padding-top: 8px; + display: grid; + gap: 6px; + } + .action-collection-grid ul, + .action-collection-grid ol { + margin: 0; + padding-left: 18px; + display: grid; + gap: 5px; + } + .action-collection-grid li { + color: var(--muted); + font-size: 13px; + overflow-wrap: anywhere; + } + .action-artifact-roles { + padding: 0; + margin: 0; + list-style: none; + display: grid; + gap: 8px; + } + .action-artifact-roles li { + border: 1px solid var(--line); + border-radius: 8px; + background: #fff; + padding: 10px; + display: grid; + gap: 4px; + min-width: 0; + } + .action-artifact-roles strong { + color: var(--ink); + font-weight: 500; + overflow-wrap: anywhere; + } + .action-artifact-roles span, + .action-artifact-roles code, + .action-artifact-roles small { + color: var(--muted); + font-size: 12px; + overflow-wrap: anywhere; + white-space: normal; + } + .action-evidence-item dl { + display: grid; + grid-template-columns: 76px minmax(0, 1fr); + gap: 6px 10px; + margin: 0; + } + .action-evidence-item dt { + color: var(--ink); + font-size: 14px; + } + .action-evidence-item dd { + margin: 0; + min-width: 0; + } + .action-evidence-checks, + .action-phase-list, + .action-command-list, + .action-repair-list, + .action-runbook-list { + margin: 0; + display: grid; + gap: 8px; + } + .action-evidence-checks, + .action-phase-list, + .action-repair-list, + .action-command-list { + padding: 0; + list-style: none; + } + .action-runbook-list { + padding-left: 20px; + } + .action-evidence-check, + .action-phase-row, + .action-repair-row, + .action-command-list li { + display: grid; + gap: 3px; + min-width: 0; + border-top: 1px solid var(--line); + padding-top: 8px; + } + .action-evidence-check.blocked { border-left: 3px solid var(--warn); padding-left: 8px; } + .action-evidence-check.pass { border-left: 3px solid var(--pass); padding-left: 8px; } + .action-phase-row.blocked { border-left: 3px solid var(--warn); padding-left: 8px; } + .action-phase-row.ready { border-left: 3px solid var(--pass); padding-left: 8px; } + .action-repair-row.blocked { border-left: 3px solid var(--warn); padding-left: 8px; } + .action-repair-row.ready { border-left: 3px solid var(--pass); padding-left: 8px; } + .action-evidence-check span, + .action-phase-row span, + .action-repair-row span, + .action-command-list span { + color: var(--ink); + font-size: 13px; + } + .action-phase-row strong, + .action-repair-row strong { + color: var(--ink); + font-size: 14px; + overflow-wrap: anywhere; + } + .action-phase-row em, + .action-repair-row em { + color: var(--muted); + font-size: 12px; + font-style: normal; + overflow-wrap: anywhere; + } + .action-evidence-check code, + .action-evidence-check small, + .action-phase-row code, + .action-phase-row small, + .action-repair-row code, + .action-repair-row small, + .action-command-list code, + .action-runbook-list li { + color: var(--muted); + font-size: 13px; + overflow-wrap: anywhere; + } + .action-command-list code { + display: block; + white-space: normal; + } + .annotation-card { + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; + background: #fff; + display: grid; + gap: 10px; + min-width: 0; + } + .annotation-card.warning { border-left: 4px solid var(--warn); } + .annotation-card.blocker { border-left: 4px solid var(--block); } + .annotation-card.resolved { opacity: .72; } + .world-evidence-grid { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 16px; + margin-top: 16px; + } + .world-evidence-card { + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; + background: #fff; + display: grid; + gap: 12px; + min-width: 0; + } + .world-evidence-card.pending { border-left: 4px solid var(--warn); } + .world-evidence-card.accepted { border-left: 4px solid var(--pass); } + .world-evidence-card span, + .world-evidence-card p, + .world-evidence-card dd, + .world-evidence-card li { + color: var(--muted); + font-size: 14px; + overflow-wrap: anywhere; + } + .world-evidence-card dl { + display: grid; + grid-template-columns: 86px minmax(0, 1fr); + gap: 6px 10px; + margin: 0; + } + .world-evidence-card dt { color: var(--ink); font-size: 14px; } + .world-evidence-card dd { margin: 0; } + .world-evidence-columns { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + border-top: 1px solid var(--line); + padding-top: 12px; + } + .world-evidence-columns h4 { + margin: 0 0 6px; + color: var(--ink); + font-size: 14px; + font-weight: 500; + } + .world-evidence-columns ul { + margin: 0; + padding-left: 18px; + } + .world-source-panel { + border-top: 1px solid var(--line); + padding-top: 12px; + } + .world-runbook-panel { + border-top: 1px solid var(--line); + padding-top: 12px; + } + .world-runbook-panel h4, + .world-source-panel h4 { + margin: 0 0 8px; + color: var(--ink); + font-size: 14px; + font-weight: 500; + } + .world-runbook-panel ul { + margin: 0; + padding-left: 18px; + } + .world-source-checks { + list-style: none; + padding: 0; + margin: 0; + display: grid; + gap: 8px; + } + .world-source-check { + display: grid; + grid-template-columns: minmax(0, .7fr) minmax(0, .6fr) minmax(0, 1fr); + gap: 10px; + align-items: start; + min-width: 0; + padding: 9px 0 0; + border-top: 1px solid var(--line); + } + .world-source-check span { + color: var(--ink); + font-size: 14px; + } + .world-source-check code, + .world-source-check small { + color: var(--muted); + font-size: 13px; + overflow-wrap: anywhere; + } + .world-source-check.pass { border-left: 3px solid var(--pass); padding-left: 8px; } + .world-source-check.blocked { border-left: 3px solid var(--warn); padding-left: 8px; } + .output-review-grid { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 16px; + margin-top: 16px; + } + .output-review-card { + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; + background: #fff; + display: grid; + gap: 12px; + min-width: 0; + } + .output-review-card.awaiting-decision, + .output-review-card.fix-decision { border-left: 4px solid var(--warn); } + .output-review-card.adjudicated { border-left: 4px solid var(--pass); } + .output-review-card span, + .output-review-card p, + .output-review-card dd, + .output-review-card li { + color: var(--muted); + font-size: 14px; + overflow-wrap: anywhere; + } + .output-review-card dl { + display: grid; + grid-template-columns: 86px minmax(0, 1fr); + gap: 6px 10px; + margin: 0; + } + .output-review-card dt { color: var(--ink); font-size: 14px; } + .output-review-card dd { margin: 0; min-width: 0; } + .output-review-steps { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + border-top: 1px solid var(--line); + padding-top: 12px; + } + .output-review-steps h4 { + margin: 0 0 6px; + color: var(--ink); + font-size: 14px; + font-weight: 500; + } + .output-review-steps ul { + margin: 0; + padding-left: 18px; + } + .output-review-commands, + .output-review-fields { + list-style: none; + padding: 0; + display: grid; + gap: 8px; + } + .output-review-commands li, + .output-review-fields li { + display: grid; + gap: 2px; + min-width: 0; + padding: 8px 0 0; + border-top: 1px solid var(--line); + } + .output-review-commands code { + overflow-wrap: anywhere; + white-space: normal; + display: block; + } + .world-intake-grid { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 16px; + margin-top: 16px; + } + .world-intake-card { + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; + background: #fff; + display: grid; + gap: 12px; + min-width: 0; + } + .world-intake-card.awaiting-submission, + .world-intake-card.fix-submission, + .world-intake-card.fix-template { border-left: 4px solid var(--warn); } + .world-intake-card.ready-for-ledger-review, + .world-intake-card.accepted { border-left: 4px solid var(--pass); } + .world-intake-card span, + .world-intake-card p, + .world-intake-card dd, + .world-intake-card li { + color: var(--muted); + font-size: 14px; + overflow-wrap: anywhere; + } + .world-intake-card dl { + display: grid; + grid-template-columns: 86px minmax(0, 1fr); + gap: 6px 10px; + margin: 0; + } + .world-intake-card dt { color: var(--ink); font-size: 14px; } + .world-intake-card dd { margin: 0; min-width: 0; } + .world-intake-steps { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + border-top: 1px solid var(--line); + padding-top: 12px; + } + .world-intake-steps h4 { + margin: 0 0 6px; + color: var(--ink); + font-size: 14px; + font-weight: 500; + } + .world-intake-steps ul { + margin: 0; + padding-left: 18px; + } + .world-intake-commands { + list-style: none; + padding: 0; + display: grid; + gap: 8px; + } + .world-intake-commands li { + display: grid; + gap: 2px; + min-width: 0; + padding: 8px 0 0; + border-top: 1px solid var(--line); + } + .world-intake-commands code { + overflow-wrap: anywhere; + white-space: normal; + display: block; + } + .section-subtitle { + margin-top: 22px; + margin-bottom: 12px; + color: var(--ink); + } + .waiver-summary { margin-top: 16px; } + .waiver-candidate-grid { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 16px; + } + .waiver-card { + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; + background: #fff; + display: grid; + gap: 12px; + min-width: 0; + } + .waiver-card.allowed { border-left: 4px solid var(--warn); } + .waiver-card.blocked { border-left: 4px solid var(--block); } + .waiver-card span, + .waiver-card p, + .waiver-card dd, + .waiver-card li { + color: var(--muted); + font-size: 14px; + overflow-wrap: anywhere; + } + .waiver-card dl { + display: grid; + grid-template-columns: 86px minmax(0, 1fr); + gap: 6px 10px; + margin: 0; + } + .waiver-card dt { + color: var(--ink); + font-size: 14px; + } + .waiver-card dd { margin: 0; min-width: 0; } + .waiver-review { + border-top: 1px solid var(--line); + padding-top: 12px; + } + .waiver-review h4 { + margin: 0 0 6px; + color: var(--ink); + font-size: 14px; + font-weight: 500; + } + .waiver-review ul { + margin: 0; + padding-left: 18px; + } + .waiver-command { + display: grid; + gap: 4px; + border-top: 1px solid var(--line); + padding-top: 12px; + } + .waiver-command span { + color: var(--ink); + font-size: 14px; + } + .waiver-command code { + display: block; + max-width: 100%; + overflow-wrap: anywhere; + white-space: normal; + } + .action-card span, + .annotation-card span, + .action-card small, + .annotation-card small, + .action-card footer, + .annotation-card footer, + .action-card dd { + color: var(--muted); + font-size: 14px; + overflow-wrap: anywhere; + } + .annotation-card dd { color: var(--muted); font-size: 14px; overflow-wrap: anywhere; } + .action-card dl, .annotation-card dl { + display: grid; + grid-template-columns: 80px minmax(0, 1fr); + gap: 6px 10px; + margin: 0; + } + .action-card dt, .annotation-card dt { color: var(--ink); font-size: 14px; } + .action-card dd, .annotation-card dd { margin: 0; } + .source-ref-list { + list-style: none; + padding: 0; + margin: 0; + display: grid; + gap: 6px; + } + .source-ref-list li { + display: grid; + gap: 2px; + min-width: 0; + padding: 8px 0 0; + border-top: 1px solid var(--line); + } + .source-ref-list a, + .source-ref-list span { + overflow-wrap: anywhere; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 13px; + } + .source-ref-list small { + font-size: 12px; + color: var(--muted); + } + .source-ref-list blockquote { + margin: 2px 0 0; + padding: 7px 9px; + border-left: 3px solid var(--line); + background: var(--soft); + color: var(--muted); + font-size: 12px; + overflow-wrap: anywhere; + } + code { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 13px; + } + @media (max-width: 980px) { + .metrics, .gates, .twocol, .actions-grid, .annotations-grid, .action-evidence-grid, .action-evidence-item dl, .action-collection-grid, .output-review-grid, .output-review-steps, .world-evidence-grid, .world-evidence-columns, .world-source-check, .world-intake-grid, .world-intake-steps, .waiver-candidate-grid, .waiver-card dl, .kv-grid { grid-template-columns: 1fr; } + main { padding: 32px 18px 60px; } + nav { justify-content: flex-start; overflow-x: auto; flex-wrap: nowrap; } + nav a { flex: 0 0 auto; } + } diff --git a/assets/review-viewer.css b/assets/review-viewer.css new file mode 100644 index 0000000..11abdb5 --- /dev/null +++ b/assets/review-viewer.css @@ -0,0 +1,176 @@ +:root { + --text: #111111; + --muted: #666666; + --line: #e8e8e8; + --soft: #f6f6f4; + --white: #ffffff; +} +* { box-sizing: border-box; } +body { + margin: 0; + background: var(--white); + color: var(--text); + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 1.6; +} +.page { + max-width: 1120px; + margin: 0 auto; + padding: 48px 32px 72px; +} +.hero { + padding-bottom: 28px; + border-bottom: 1px solid var(--line); + margin-bottom: 28px; +} +h1, h2, h3 { + margin: 0 0 12px; + letter-spacing: -0.02em; + font-weight: 600; +} +h1 { font-size: 40px; line-height: 1.08; } +h2 { font-size: 22px; margin-top: 34px; } +h3 { font-size: 16px; } +p, li, span { font-size: 15px; } +.lede { + max-width: 860px; + font-size: 18px; + color: var(--muted); + margin: 0 0 18px; +} +.meta { + display: flex; + gap: 10px; + flex-wrap: wrap; + margin-top: 16px; +} +.meta span { + border: 1px solid var(--line); + padding: 6px 10px; + background: var(--soft); +} +.arch-grid { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 12px; + margin-top: 16px; +} +.arch-step, .panel, .direction-card, .baseline-box { + border: 1px solid var(--line); + background: var(--white); +} +.arch-step { + padding: 14px; + min-height: 132px; +} +.step-label { + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--muted); + margin-bottom: 10px; +} +.step-detail { + font-size: 14px; +} +.grid { + display: grid; + grid-template-columns: 1.1fr 0.9fr; + gap: 18px; + margin-top: 16px; +} +.panel { + padding: 18px; +} +.panel ul { + margin: 0; + padding-left: 18px; +} +.direction-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; + margin-top: 16px; +} +.variant-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; + margin-top: 16px; +} +.direction-card { + padding: 18px; +} +.direction-card ul { + margin: 12px 0; + padding-left: 18px; +} +.minor { + color: var(--muted); + font-size: 13px; +} +.variant-card { + border: 1px solid var(--line); + background: var(--white); + padding: 18px; +} +.variant-head { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: baseline; +} +.variant-head span { + color: var(--muted); + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.variant-description { + margin: 14px 0; + padding-left: 14px; + border-left: 2px solid var(--line); + color: var(--text); +} +.variant-metrics { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 14px; +} +.variant-metrics span { + border: 1px solid var(--line); + background: var(--soft); + padding: 6px 10px; + font-size: 12px; +} +.variant-cues p { + margin: 8px 0 6px; +} +.variant-cues ul { + margin: 0 0 12px; + padding-left: 18px; +} +table { + width: 100%; + border-collapse: collapse; + margin-top: 14px; + font-size: 14px; +} +th, td { + border-top: 1px solid var(--line); + text-align: left; + padding: 10px 8px; + vertical-align: top; +} +th { + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--muted); +} +@media (max-width: 1000px) { + .arch-grid, .direction-grid, .variant-grid, .grid { + grid-template-columns: 1fr; + } +} diff --git a/assets/skill-overview.css b/assets/skill-overview.css new file mode 100644 index 0000000..b626f5e --- /dev/null +++ b/assets/skill-overview.css @@ -0,0 +1,735 @@ +:root { + --paper: #ffffff; + --wash: #f8fafc; + --wash-strong: #f2f5f8; + --line: #e6e0d4; + --line-soft: #eee9df; + --brand: #1B365D; + --brand-soft: #EEF3F8; + --brand-mid: #315982; + --ink: #151515; + --text: #2f2d29; + --muted: #68625a; + --faint: #8b857b; + --success: #2f6f5e; + --warn: #8a5a19; + --serif: "TsangerJinKai02", "Source Han Serif SC", "Noto Serif CJK SC", "Songti SC", "STSong", Charter, Georgia, serif; + --mono: "JetBrains Mono", "SF Mono", ui-monospace, Menlo, Consolas, monospace; + --shadow-soft: 0 1px 2px rgba(27, 54, 93, 0.06), 0 16px 44px rgba(27, 54, 93, 0.08); + } + * { box-sizing: border-box; } + html { scroll-behavior: smooth; } + body { + margin: 0; + background: #ffffff; + color: var(--ink); + font-family: var(--serif); + line-height: 1.62; + letter-spacing: 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + } + body[data-report-lang="zh-CN"] [data-lang="en"], + body[data-report-lang="en"] [data-lang="zh-CN"] { display: none !important; } + .skip-link { + position: fixed; + left: 18px; + top: 10px; + z-index: 40; + transform: translateY(-140%); + padding: 8px 12px; + border-radius: 8px; + background: var(--brand); + color: #fff; + text-decoration: none; + transition: transform 160ms cubic-bezier(0.16, 1, 0.3, 1); + } + .skip-link:focus-visible { transform: translateY(0); outline: 2px solid var(--brand-mid); outline-offset: 2px; } + .topbar { + position: sticky; + top: 0; + z-index: 20; + background: rgba(255, 255, 255, 0.96); + border-bottom: 1px solid var(--line); + } + .progress-track { + height: 2px; + background: transparent; + } + .progress-bar { + display: block; + width: 100%; + height: 100%; + background: var(--brand); + transition: transform 160ms cubic-bezier(0.16, 1, 0.3, 1); + transform-origin: left center; + transform: scaleX(0); + } + .topbar-inner { + max-width: 1240px; + margin: 0 auto; + padding: 9px 28px; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 18px; + align-items: center; + } + .nav-shell { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 18px; + align-items: center; + min-width: 0; + } + .report-mark { + color: var(--brand); + font-family: var(--mono); + font-size: 11px; + line-height: 1; + text-transform: uppercase; + white-space: nowrap; + } + .report-nav { + display: flex; + gap: 4px; + overflow-x: auto; + scrollbar-width: none; + min-width: 0; + } + .report-nav::-webkit-scrollbar { display: none; } + .report-nav a { + flex: 0 0 auto; + min-width: 76px; + min-height: 40px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 10px; + color: var(--brand); + text-decoration: none; + text-align: center; + font-size: 13px; + border-radius: 8px; + white-space: nowrap; + transition-property: background-color, color, transform; + transition-duration: 160ms; + transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1); + } + @media (hover: hover) { + .report-nav a:hover { background: var(--brand-soft); } + } + .report-nav a:focus-visible { + outline: 2px solid var(--brand-mid); + outline-offset: 2px; + background: var(--brand-soft); + } + .report-nav a[aria-current="true"] { + background: var(--brand); + color: #ffffff; + } + .report-nav a[aria-current="true"] span { color: #ffffff; } + .language-switch { + display: inline-flex; + gap: 3px; + padding: 3px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--paper); + box-shadow: 0 1px 2px rgba(27, 54, 93, 0.05); + } + .language-switch button { + appearance: none; + min-width: 42px; + min-height: 34px; + border: 0; + border-radius: 6px; + padding: 0 10px; + background: transparent; + color: var(--muted); + font: inherit; + font-size: 13px; + cursor: pointer; + transition-property: background-color, color, transform; + transition-duration: 160ms; + transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1); + } + .language-switch button:active { transform: scale(0.96); } + .language-switch button:focus-visible { outline: 2px solid var(--brand-mid); outline-offset: 2px; } + .language-switch button[aria-pressed="true"] { + background: var(--brand-soft); + color: var(--brand); + } + .wrap { + max-width: 1240px; + margin: 0 auto; + padding: 58px 28px 92px; + } + .hero { + padding: 16px 0 34px; + } + .hero-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) 360px; + gap: 44px; + align-items: end; + } + .eyebrow, .report-mark { + letter-spacing: 0; + } + .eyebrow { + margin: 0 0 12px; + color: var(--brand); + font-family: var(--mono); + font-size: 12px; + text-transform: uppercase; + } + .slug { + margin: 0 0 18px; + color: var(--faint); + font-family: var(--mono); + font-size: 13px; + overflow-wrap: anywhere; + } + h1, h2, h3 { + margin: 0; + font-weight: 500; + letter-spacing: 0; + text-wrap: balance; + } + h1 { + max-width: 8em; + color: var(--ink); + font-size: 4rem; + line-height: 1.02; + } + .lead { + max-width: 760px; + margin: 20px 0 0; + color: var(--text); + font-size: 1.08rem; + text-wrap: pretty; + } + .hero-meta, .badges { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 20px; + } + .hero-meta span, .badges span, .tag { + display: inline-flex; + align-items: center; + min-height: 30px; + padding: 5px 10px; + border-radius: 6px; + background: var(--brand-soft); + color: var(--brand); + font-size: 13px; + font-variant-numeric: tabular-nums; + } + .hero-card { + position: relative; + padding: 22px 22px 24px; + border-radius: 8px; + background: linear-gradient(180deg, #ffffff 0%, #fbfaf7 100%); + box-shadow: var(--shadow-soft); + border: 1px solid var(--line-soft); + } + .hero-card::before { + content: ""; + position: absolute; + top: 18px; + right: 18px; + width: 38px; + height: 2px; + background: var(--brand); + } + .score-strip { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; + margin-top: 32px; + padding-top: 20px; + border-top: 1px solid var(--line); + } + .score-chip { + padding: 14px 14px 12px; + border-radius: 8px; + background: var(--wash); + border: 1px solid var(--line-soft); + } + .score-chip span { + display: block; + color: var(--muted); + font-size: 12px; + } + .score-chip strong { + display: block; + margin: 5px 0 8px; + color: var(--brand); + font-family: var(--mono); + font-size: 1.65rem; + font-weight: 500; + line-height: 1; + font-variant-numeric: tabular-nums; + } + .score-chip i { + display: block; + height: 3px; + border-radius: 999px; + background: linear-gradient(90deg, var(--brand) var(--score), #dfe6ee var(--score)); + } + .score-chip small { + display: block; + margin-top: 8px; + color: var(--muted); + font-size: 12px; + line-height: 1.45; + } + section { + scroll-margin-top: 78px; + padding-top: 44px; + margin-top: 44px; + border-top: 1px solid var(--line); + } + section.hero { + scroll-margin-top: 0; + padding-top: 16px; + margin-top: 0; + border-top: 0; + } + .section-head { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 22px; + align-items: start; + } + h2 { + color: var(--ink); + font-size: 1.5rem; + line-height: 1.18; + } + h2::before { + content: ""; + display: block; + width: 32px; + height: 2px; + margin-bottom: 12px; + background: var(--brand); + } + h3 { + color: var(--ink); + font-size: 1.02rem; + line-height: 1.3; + margin-bottom: 10px; + } + .section-head > div:first-child p { + margin: 12px 0 0; + color: var(--muted); + max-width: 68ch; + text-wrap: pretty; + } + .section-body { + margin-top: 24px; + } + .two-col, .metric-grid, .chart-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + align-items: stretch; + } + .quality-panels { + margin-top: 16px; + } + .metrics-flow { + display: grid; + gap: 30px; + } + .metrics-primary { + display: grid; + grid-template-columns: minmax(360px, 0.92fr) minmax(0, 1.08fr); + gap: 22px; + align-items: start; + } + .metrics-primary .chart-figure { + min-height: 0; + display: grid; + align-content: center; + } + .metrics-primary .chart-figure svg { + max-height: 520px; + } + .metrics-note { + display: grid; + align-content: start; + gap: 14px; + } + .metrics-note p { + margin: 0; + max-width: none; + color: var(--text); + } + .metric-summary-list { + margin: 0; + padding: 0; + list-style: none; + display: grid; + gap: 10px; + } + .metric-summary-list li { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + gap: 8px 10px; + align-items: baseline; + padding: 10px 0; + border-bottom: 1px solid var(--line-soft); + } + .metric-summary-list li:last-child { border-bottom: 0; } + .metric-summary-list b { + color: var(--ink); + font-weight: 500; + } + .metric-summary-list em { + color: var(--brand); + font-family: var(--mono); + font-style: normal; + font-variant-numeric: tabular-nums; + } + .metric-summary-list small { + grid-column: 2 / 4; + color: var(--muted); + line-height: 1.5; + overflow-wrap: break-word; + } + .metric-detail-section { + display: grid; + gap: 14px; + } + .detail-section-kicker { + display: inline-flex; + width: fit-content; + min-height: 28px; + align-items: center; + padding: 3px 8px; + border-radius: 6px; + background: var(--brand-soft); + color: var(--brand); + font-size: 12px; + } + .metric-status { + display: inline-flex; + align-items: center; + min-height: 24px; + padding: 2px 7px; + border-radius: 6px; + background: var(--brand-soft); + color: var(--brand); + font-size: 12px; + } + .metric-detail-grid { + grid-template-columns: repeat(2, minmax(min(100%, 420px), 1fr)); + align-items: stretch; + } + .list, .compact-list, .step-list { + margin: 0; + padding-left: 1.15em; + display: grid; + gap: 8px; + } + .list li::marker, .compact-list li::marker, .step-list li::marker { color: var(--brand); } + .compact-list { + gap: 6px; + font-size: 13.5px; + color: var(--muted); + line-height: 1.55; + } + .compact-list li, .list li, .step-list li { + min-width: 0; + overflow-wrap: break-word; + } + .panel, .metric-card, .roadmap-item { + background: #ffffff; + border: 1px solid var(--line-soft); + border-radius: 8px; + padding: 22px; + box-shadow: 0 1px 2px rgba(27, 54, 93, 0.04); + min-width: 0; + } + .metric-card { + display: grid; + grid-template-columns: minmax(112px, 0.28fr) minmax(0, 1fr); + gap: 18px; + align-content: start; + min-height: 0; + } + .metric-card-head { + display: grid; + gap: 10px; + align-content: start; + } + .metric-card strong { + display: block; + margin: 0; + color: var(--brand); + font-family: var(--mono); + font-size: 2rem; + font-weight: 500; + line-height: 1; + font-variant-numeric: tabular-nums; + } + .metric-label { + color: var(--muted); + font-size: 13px; + line-height: 1.45; + overflow-wrap: break-word; + } + .metric-card-body { + min-width: 0; + } + .metric-card-body .compact-list { + gap: 7px; + max-width: 76ch; + } + .metric-card-body .compact-list li { + overflow-wrap: break-word; + word-break: normal; + } + .chart-figure { + margin: 0; + padding: 18px; + border: 1px solid var(--line-soft); + border-radius: 8px; + background: #ffffff; + box-shadow: 0 1px 2px rgba(27, 54, 93, 0.04); + } + .chart-figure svg { + width: 100%; + height: auto; + display: block; + overflow: visible; + } + .chart-figure text { + fill: var(--ink); + font-family: var(--serif); + font-size: 13px; + } + .chart-title { + fill: var(--brand); + font-size: 16px; + font-weight: 500; + } + .chart-line { + fill: none; + stroke: var(--brand); + stroke-width: 2; + } + figcaption { + margin-top: 12px; + padding-top: 10px; + border-top: 1px solid var(--line-soft); + color: var(--muted); + font-size: 13px; + line-height: 1.5; + text-wrap: pretty; + } + table { + width: 100%; + border-collapse: collapse; + font-size: 14px; + } + th, td { + padding: 12px 10px; + text-align: left; + border-bottom: 1px solid var(--line-soft); + vertical-align: top; + } + th { + color: var(--brand); + font-weight: 500; + font-size: 12px; + } + td:first-child, th:first-child { width: 96px; } + .world-readiness { + margin-top: 16px; + display: grid; + gap: 16px; + } + .section-head > .world-readiness { grid-column: 1; } + .world-readiness-head { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 16px; + align-items: start; + } + .world-readiness-head p { + margin-top: 8px; + max-width: 72ch; + } + .world-status { + display: inline-flex; + align-items: center; + min-height: 30px; + padding: 4px 10px; + border-radius: 6px; + background: var(--brand-soft); + color: var(--brand); + font-size: 13px; + white-space: nowrap; + } + .evidence-kpis { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + } + .evidence-kpi { + padding: 14px; + border: 1px solid var(--line-soft); + border-radius: 8px; + background: var(--wash); + min-width: 0; + } + .evidence-kpi span { + display: block; + color: var(--muted); + font-size: 12px; + } + .evidence-kpi strong { + display: block; + margin-top: 4px; + color: var(--brand); + font-family: var(--mono); + font-size: 1.5rem; + font-weight: 500; + line-height: 1; + font-variant-numeric: tabular-nums; + } + .evidence-kpi small { + display: block; + margin-top: 8px; + color: var(--muted); + line-height: 1.45; + overflow-wrap: break-word; + } + .evidence-list { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + } + .evidence-item { + padding: 14px; + border: 1px solid var(--line-soft); + border-radius: 8px; + background: #ffffff; + min-width: 0; + } + .evidence-item span { + color: var(--brand); + font-family: var(--mono); + font-size: 11px; + text-transform: uppercase; + } + .evidence-item h4 { + margin: 4px 0 8px; + color: var(--ink); + font-size: 1rem; + font-weight: 500; + line-height: 1.3; + } + .evidence-item h5 { + margin: 12px 0 6px; + color: var(--brand); + font-size: 12px; + font-weight: 500; + } + .evidence-item p { + margin: 0; + max-width: none; + color: var(--muted); + overflow-wrap: break-word; + } + .blocked-checks { + margin: 0; + padding-left: 1.05em; + color: var(--muted); + font-size: 13px; + line-height: 1.45; + } + .blocked-checks li { + overflow-wrap: break-word; + } + .blocked-checks li::marker { + color: var(--brand); + } + .blocked-checks-empty { + font-size: 13px; + } + .roadmap { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; + } + .step { + display: inline-flex; + margin-bottom: 12px; + min-height: 28px; + align-items: center; + padding: 3px 8px; + border-radius: 6px; + background: var(--brand-soft); + color: var(--brand); + font-size: 12px; + } + .unlock { + margin-top: 14px; + color: var(--muted); + font-size: 13px; + } + @media (max-width: 980px) { + .topbar-inner { + grid-template-columns: minmax(0, 1fr) auto; + padding: 8px 16px; + } + .nav-shell { grid-template-columns: 1fr; gap: 6px; } + .report-mark { display: none; } + .report-nav a { min-width: 72px; } + .hero-grid, .section-head { grid-template-columns: 1fr; } + .section-head > .world-readiness { grid-column: auto; } + .hero-grid { gap: 24px; } + .score-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .metrics-primary, .metric-detail-grid, .two-col, .metric-grid, .chart-grid, .roadmap { grid-template-columns: 1fr; } + .evidence-list { grid-template-columns: 1fr; } + .metric-card { grid-template-columns: 1fr; } + h1 { font-size: 2.75rem; } + } + @media (max-width: 540px) { + .wrap { padding: 34px 16px 72px; } + .topbar-inner { gap: 10px; } + .report-nav { + display: flex; + gap: 2px; + } + .report-nav a { + min-width: 66px; + min-height: 38px; + padding: 0 8px; + font-size: 12px; + } + .language-switch button { min-width: 38px; min-height: 32px; padding: 0 8px; } + .hero { padding-top: 8px; } + h1 { max-width: 8em; font-size: 2.35rem; } + .lead { font-size: 1rem; } + .hero-meta span, .badges span { font-size: 12px; } + .score-strip { grid-template-columns: 1fr; } + section { margin-top: 34px; padding-top: 34px; } + .panel, .metric-card, .roadmap-item, .chart-figure, .hero-card { padding: 18px; } + .world-readiness-head, .evidence-kpis { grid-template-columns: 1fr; } + .metric-card { grid-template-columns: 1fr; gap: 10px; } + .metric-card strong { margin-bottom: 2px; } + .metric-summary-list li { grid-template-columns: auto minmax(0, 1fr) auto; } + table { font-size: 13px; } + th, td { padding: 10px 7px; } + } + @media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + *, *::before, *::after { transition-duration: 0.01ms !important; } + } diff --git a/assets/skill-overview.js b/assets/skill-overview.js new file mode 100644 index 0000000..eaf9ef4 --- /dev/null +++ b/assets/skill-overview.js @@ -0,0 +1,37 @@ +(function () { + var buttons = Array.prototype.slice.call(document.querySelectorAll("[data-set-lang]")); + var navLinks = Array.prototype.slice.call(document.querySelectorAll(".report-nav a")); + var sections = navLinks + .map(function (link) { return document.querySelector(link.getAttribute("href")); }) + .filter(Boolean); + var progressBar = document.querySelector(".progress-bar"); + function setLanguage(lang) { + document.body.setAttribute("data-report-lang", lang); + document.documentElement.setAttribute("lang", lang); + buttons.forEach(function (button) { + button.setAttribute("aria-pressed", button.getAttribute("data-set-lang") === lang ? "true" : "false"); + }); + } + function updateProgress() { + var scrollTop = window.scrollY || document.documentElement.scrollTop; + var height = Math.max(1, document.documentElement.scrollHeight - window.innerHeight); + var pct = Math.min(100, Math.max(0, (scrollTop / height) * 100)); + if (progressBar) progressBar.style.transform = "scaleX(" + pct / 100 + ")"; + var active = sections[0]; + sections.forEach(function (section) { + if (section.getBoundingClientRect().top <= 110) active = section; + }); + navLinks.forEach(function (link) { + link.setAttribute("aria-current", link.getAttribute("href") === "#" + active.id ? "true" : "false"); + }); + } + buttons.forEach(function (button) { + button.addEventListener("click", function () { + setLanguage(button.getAttribute("data-set-lang")); + }); + }); + window.addEventListener("scroll", updateProgress, { passive: true }); + window.addEventListener("resize", updateProgress); + setLanguage("zh-CN"); + updateProgress(); + })(); diff --git a/docs/README.fr-FR.md b/docs/README.fr-FR.md new file mode 100644 index 0000000..ec17d4f --- /dev/null +++ b/docs/README.fr-FR.md @@ -0,0 +1,240 @@ +# Présentation de Yao Meta Skill + +`YAO = Yielding AI Outcomes` signifie produire de vrais résultats grâce à l'IA. L'objectif n'est pas d'écrire davantage de texte de prompt, mais de livrer des actifs IA réutilisables et des résultats opérationnels concrets. + +`yao-meta-skill` est un système léger mais rigoureux pour créer, évaluer, empaqueter et gouverner des agent skills réutilisables. + +Il transforme des workflows bruts, des transcripts, des prompts, des notes et des runbooks en paquets de skills réutilisables avec : + +- une surface de déclenchement claire +- un `SKILL.md` léger +- des references, scripts et evals optionnels +- un dialogue d'intention plus humain avant l'authoring approfondi, avec un intent confidence gate qui continue à clarifier si le vrai travail, la sortie, les exclusions ou les standards restent flous +- un benchmark scan GitHub silencieux par défaut, complété par une reference synthesis, qui étudie des dépôts publics de haute qualité et des patterns world-class, puis ne remonte à l'utilisateur que les vrais conflits ou les zones d'incertitude +- une demande explicite de références fournies par l'utilisateur quand elles existent, afin d'apprendre des modèles, pas de copier le texte ni du contenu privé +- un rapport HTML minimaliste en fond blanc généré automatiquement pour chaque nouveau skill +- un prompt quality profile qui transforme le need model, le mapping RTF, la complexité et les quality checks en preuves visibles pour le reviewer, sans alourdir `SKILL.md` +- trois directions d'itération à plus forte valeur après la première création +- un review viewer HTML compact pour accélérer la première revue humaine +- un feedback log léger pour éviter de lancer tout le flux de promotion à chaque tour +- un rapport with-skill vs baseline pour visualiser rapidement le gain incrémental +- un quickstart conversationnel, sensible aux archetypes, pour orienter un nouveau skill vers scaffold, production, library ou governed +- des métadonnées sources neutres et des adaptateurs spécifiques au client +- des contrôles de gouvernance, de promotion et de portabilité intégrés au flux standard + +## Architecture + +En version hero, le système tient sur une seule ligne : transformer une entrée brouillonne en skill package gouverné et réutilisable. + +```mermaid +flowchart LR + A["Entrées
workflow / prompt / transcript / docs / notes"] --> B["Routage
SKILL.md"] + B --> C["Conception
méthode + garde-fous"] + C --> D["Exécution
create / validate / eval / promote"] + D --> E["Sorties
skill package + rapports + adapters"] +``` + +Lecture en 10 secondes : + +- **Entrées** : on part de workflows, prompts, documents et notes dispersés. +- **Routage** : un `SKILL.md` léger définit d'abord la frontière et le déclenchement. +- **Conception** : on choisit le bon archetype, les bons gates et la bonne séparation des ressources. +- **Exécution** : la CLI unifiée construit, valide, optimise et promeut le skill. +- **Sorties** : on obtient un skill package réutilisable avec ses preuves d'évaluation, de gouvernance et de portabilité. + +## Benchmark qualité pondéré + +Ce benchmark est une revue d'ingénierie du projet. Chaque dimension est notée de `0-10`, puis pondérée sur `100`. Les GitHub stars ne sont pas incluses, car elles mesurent la chaleur de l'écosystème, pas directement la qualité d'ingénierie d'une méta-skill. + +Formule du score pondéré : `sum(score / 10 * poids)`. + +| Méta-skill | Méthode 15 | Discipline contexte 10 | Toolchain 15 | Eval/tests 20 | Gouvernance 15 | Portabilité 10 | Onboarding/revue 5 | Fiabilité locale 10 | Score pondéré | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| Yao Meta Skill | 9.5 | 8.0 | 9.5 | 9.5 | 9.5 | 9.0 | 6.5 | 9.5 | 91.5 | +| Anthropic Skill Creator | 9.0 | 6.5 | 8.5 | 7.5 | 4.0 | 5.0 | 7.5 | 5.0 | 67.5 | +| OpenAI Skill Creator | 8.5 | 9.5 | 5.0 | 2.0 | 3.0 | 4.0 | 8.5 | 4.0 | 50.5 | + +| Rang | Méta-skill | Score | Positionnement central | +| ---: | --- | ---: | --- | +| 1 | Yao Meta Skill | 91.5 | Système complet d'ingénierie, d'évaluation, de gouvernance et de portabilité pour skills réutilisables. | +| 2 | Anthropic Skill Creator | 67.5 | Méthode et boucle d'itération fortes, mais fiabilité locale et gouvernance plus faibles. | +| 3 | OpenAI Skill Creator | 50.5 | Très utile comme guide concis d'écriture de skills, moins comme système d'ingénierie complet. | + +## Scénarios recommandés + +- Choisissez **Yao Meta Skill** si vous voulez un actif d'équipe réutilisable avec frontières explicites, gates d'évaluation, gouvernance, portabilité et maintenance à long terme. +- Choisissez **Anthropic Skill Creator** si vous voulez une boucle de création d'abord conversationnelle et une itération guidée par l'humain. +- Choisissez **OpenAI Skill Creator** si vous cherchez surtout une référence concise pour écrire des skills légères avec une forte discipline de contexte. +- Un schéma hybride utile consiste à produire un premier jet avec un creator conversationnel, puis à utiliser `yao-meta-skill` pour durcir le package et le rendre prêt pour une équipe. + +## Démarrage rapide + +Pour utiliser cette skill directement dans Codex, installez-la d'abord dans les skills globales : + +```bash +npx -y skills add yaojingang/yao-meta-skill -a codex -g -y +``` + +Pour l'installer dans tous les agents pris en charge, remplacez `-a codex` par `-a '*'` : + +```bash +npx -y skills add yaojingang/yao-meta-skill -a '*' -g -y +``` + +Après l'installation, redémarrez le client. Demandez ensuite des tâches comme "create a skill from this workflow", "improve this existing skill", "evaluate this skill" ou "add evals to this skill" pour déclencher `yao-meta-skill`. + +1. Décrivez le workflow, l'ensemble de prompts ou la tâche répétée que vous voulez transformer en skill. +2. Commencez par un court dialogue d'intention plus humain pour clarifier le vrai travail, les sorties attendues, les exclusions, les contraintes et les standards qui comptent pour vous. +3. Laissez d'abord `quickstart` clarifier l'intention, puis lancer silencieusement benchmark scan et reference synthesis ; des questions explicites ne remontent que si l'intention reste ambiguë ou si deux directions de conception se contredisent réellement. +4. Utilisez le `quickstart` sensible aux archetypes ou le flux complet d'authoring pour générer ou améliorer le paquet en mode scaffold, production, library ou governed. +5. Chaque nouveau skill reçoit aussi `reports/intent-dialogue.md`, `reports/intent-confidence.md`, `reports/reference-synthesis.md`, `reports/artifact-design-profile.md`, `reports/prompt-quality-profile.md`, `reports/skill-overview.html`, `reports/review-viewer.html` et `reports/iteration-directions.md`. Ensuite, le feedback log et le baseline compare permettent de boucler rapidement sans lancer tout le flux de promotion. + +## Résultats actuels + +- `make test` passe actuellement +- sur le jeu de régression courant, trigger eval a `0` faux positifs et `0` faux négatifs +- les suites train / dev / holdout passent toutes +- les expressions chinoises réelles sont maintenant couvertes dans trigger eval, par exemple `做一个 skill`, `沉淀成可复用能力`, `优化已有 skill`, `补 trigger 评测` +- les contrats de packaging `openai`, `claude` et `generic` sont validés + +## Points forts actuels + +Dans la dernière revue pondérée, Yao atteint `91.5/100`. Les points forts se concentrent sur ce qui rend une skill durablement exploitable par une équipe : + +- **Profondeur méthodologique `9.5`** : doctrine de skill engineering, archetypes, gate selection, non-skill decisions, gouvernance et frontières de ressource sont formalisés. +- **Complétude de la toolchain `9.5`** : initialisation, validation, benchmark scan, description optimization, reporting, contrôle de promotion, packaging, CI et portability checks sont reliés dans un même flux. +- **Rigueur Eval / tests `9.5`** : train/dev/holdout, blind holdout, adversarial holdout, judge-backed blind eval, route confusion, drift history et promotion gates sont couverts. +- **Gouvernance / cycle de vie `9.5`** : les skills importantes peuvent porter owner, lifecycle, review cadence, maturity score, trust boundary, promotion decision et regression history. +- **Fiabilité d'exécution locale `9.5`** : le dépôt est vérifiable localement avec `make test`, `make ci-test` et la CLI unifiée. +- **Portabilité / distribution `9.0`** : métadonnées neutres, adaptateurs, règles de dégradation, contrats de packaging et score de portabilité conservent la sémantique réutilisable entre environnements. +- **Discipline de contexte `8.0`** : le point d'entrée reste sous budget, mais cette contrainte est suivie activement car le système porte davantage de rapports, exemples, benchmarks et preuves. +- **Onboarding / revue `6.5`** : quickstart, HTML overview, side-by-side review viewer et feedback log ont amélioré la première expérience, mais c'est encore la principale zone d'amélioration UX. + +La direction est volontaire : garder une entrée légère, rendre l'évaluation difficile à simuler, rendre la gouvernance visible, et réduire encore la friction de création et de revue. + +## Pourquoi Yao + +- **Léger** : le point d'entrée reste compact, les budgets de contexte sont explicites, et la structure supplémentaire n'est ajoutée que lorsqu'elle apporte une vraie valeur. +- **Rigoureux** : la qualité de déclenchement est vérifiée par family regressions, blind holdout, adversarial holdout, route confusion, judge-backed blind eval et promotion gates. +- **Gouvernable** : les skills importantes sont traitées comme des actifs maintenables avec lifecycle, attentes de maturité, ownership et cadence de revue. +- **Portable** : les métadonnées source restent neutres, tandis que les adaptateurs, règles de dégradation et contrats de packaging préservent une sémantique réutilisable entre environnements. + +## Ce que fait le projet + +Ce projet permet de créer, refactoriser, évaluer et empaqueter des skills comme des briques de capacité durables plutôt que comme des prompts ponctuels. + +Sa logique de conception est simple : + +1. identifier le vrai travail récurrent derrière la demande +2. définir une frontière propre pour que chaque paquet fasse un travail cohérent +3. optimiser la description de déclenchement avant d'allonger le corps +4. garder le fichier principal compact et déplacer le détail vers les references ou les scripts +5. ajouter des garde-fous qualité seulement lorsqu'ils sont utiles +6. exporter des artefacts de compatibilité uniquement pour les clients nécessaires + +## Pourquoi ce projet existe + +Dans la plupart des équipes, la connaissance opérationnelle utile est dispersée dans les chats, les prompts personnels, les habitudes orales et les workflows non documentés. Ce projet convertit cette connaissance implicite en : + +- paquets de skills découvrables +- flux d'exécution répétables +- instructions à faible coût de contexte +- actifs réutilisables pour l'équipe +- distributions prêtes pour la compatibilité + +## Structure du dépôt + +```text +yao-meta-skill/ +├── SKILL.md +├── README.md +├── LICENSE +├── .gitignore +├── agents/ +│ └── interface.yaml +├── references/ +├── scripts/ +└── templates/ +``` + +## Composants principaux + +### `SKILL.md` + +Le point d'entrée principal. Il définit la surface de déclenchement, les modes opératoires, le workflow compact et le contrat de sortie. + +### `agents/interface.yaml` + +La source de vérité neutre pour les métadonnées. Ce fichier stocke les informations d'affichage et de compatibilité sans lier l'arborescence source à un chemin spécifique à un fournisseur. + +### `references/` + +Les documents longs qui ne doivent pas gonfler le fichier principal. On y trouve les règles de conception, les guides d'évaluation, la stratégie de compatibilité et les rubriques de qualité. + +### `scripts/` + +Des utilitaires qui rendent la méta-skill opérationnelle : + +- `trigger_eval.py` : vérifie si une description de déclenchement est trop large ou trop faible +- `context_sizer.py` : estime le poids de contexte et avertit si le chargement initial devient trop grand +- `cross_packager.py` : génère des artefacts d'export spécifiques au client à partir du paquet source neutre + +### `templates/` + +Des modèles de départ pour des paquets simples ou plus avancés. + +## Comment l'utiliser + +### 1. Utiliser directement la skill + +Invoquez `yao-meta-skill` lorsque vous souhaitez : + +- créer une nouvelle skill +- améliorer une skill existante +- ajouter des evals à une skill +- transformer un workflow en paquet réutilisable +- préparer une skill pour une adoption plus large dans l'équipe + +### 2. Générer un nouveau paquet de skill + +Le flux typique est : + +1. décrire le workflow ou la capacité +2. identifier les phrases de déclenchement et les sorties attendues +3. choisir le mode scaffold, production ou library +4. générer le paquet +5. lancer les vérifications de taille et de déclenchement si nécessaire +6. exporter les artefacts de compatibilité ciblés + +### 3. Exporter des artefacts de compatibilité + +Exemples : + +```bash +python3 scripts/cross_packager.py ./yao-meta-skill --platform openai --platform claude --zip +python3 scripts/context_sizer.py ./yao-meta-skill +python3 scripts/trigger_eval.py --description "Create and improve agent skills..." --cases ./cases.json +``` + +## Avantages + +- **Méthode d'abord, pas prompt d'abord** : la création de skill est traitée comme un workflow d'ingénierie formel +- **Pensé pour l'optimisation du déclenchement** : les descriptions passent par route confusion, blind holdout, familles adversariales et promotion policy +- **Léger au point d'entrée** : `SKILL.md` reste compact et les references, scripts, evals ne sont ajoutés que lorsqu'ils sont utiles +- **Outillage cohérent** : initialisation, validation, optimisation, reporting, packaging et test passent par un CLI et un chemin CI unifiés +- **Gouverné comme un actif** : les skills importantes peuvent porter ownership, lifecycle, attentes de maturité et cadence de revue +- **Portable par défaut** : les sources restent neutres et la compatibilité est gérée par des adaptateurs et des règles de dégradation +- **Riche en preuves** : route scorecards, regression history, context budgets, portability scores et promotion decisions sont publiés comme artefacts + +## Public idéal + +Ce projet est particulièrement adapté à : + +- constructeurs d'agents +- équipes d'outillage interne +- prompt engineers évoluant vers des skills structurées +- organisations construisant des bibliothèques de skills réutilisables + +## Licence + +MIT. Voir [LICENSE](../LICENSE). diff --git a/docs/README.ja-JP.md b/docs/README.ja-JP.md new file mode 100644 index 0000000..4b48e2d --- /dev/null +++ b/docs/README.ja-JP.md @@ -0,0 +1,240 @@ +# Yao Meta Skill 日本語紹介 + +`YAO = Yielding AI Outcomes` は、「AI の結果を実際の成果として届ける」という意味です。単に prompt テキストを増やすのではなく、再利用可能な AI 資産と実運用の成果を作ることを重視します。 + +`yao-meta-skill` は、再利用可能な agent skill を作成・評価・パッケージ化・運用するための、軽量で厳密なシステムです。 + +粗い workflow、transcript、prompt、notes、runbook を、再利用可能な skill パッケージに変換し、次の性質を持たせます。 + +- 明確なトリガー面 +- 軽量な `SKILL.md` +- 必要に応じた references、scripts、evals +- 深い authoring の前に、人間味のある短い intent dialogue を行い、intent confidence gate で理解が十分かを判定する。不十分なら 1〜2 個の高レバレッジ質問を追加する +- 深い authoring の前に、GitHub benchmark scan と reference synthesis を静かに実行し、高品質な公開リポジトリと world-class pattern tracks を参照する。実際に衝突や不確実性があるときだけユーザーに明示する +- ユーザー自身の参考例があれば、それも取り込み、文章ではなくパターン・構造・品質基準だけを学ぶ +- 新しい skill ごとに、白背景の簡潔な HTML overview を自動生成 +- prompt quality profile により、need model、RTF mapping、complexity、prompt-facing quality checks を reviewer-visible な証拠として残し、`SKILL.md` を肥大化させない +- 初回作成後に自動で提示される 3 つの高価値な次の iteration direction +- 初回の人間レビューを助ける、コンパクトな HTML review viewer +- 毎回フル promotion flow を通さずに使える軽量 feedback log +- 増分価値をすばやく確認できる with-skill vs baseline 比較レポート +- 会話型 discovery flow として scaffold / production / library / governed を案内する archetype-aware quickstart +- 中立的なソースメタデータとクライアント別アダプタ +- ガバナンス、昇格判定、portability チェックを標準フローに内蔵 + +## アーキテクチャ + +Hero 版では、全体の流れは 1 本です。ばらばらの入力を、統制された再利用可能な skill package に変えます。 + +```mermaid +flowchart LR + A["入力
workflow / prompt / transcript / docs / notes"] --> B["ルート定義
SKILL.md"] + B --> C["設計
method + gates"] + C --> D["実行
create / validate / eval / promote"] + D --> E["出力
skill package + reports + adapters"] +``` + +10 秒で読むならこうです。 + +- **入力**: workflow、prompt、文書、メモなどの粗い素材から始めます。 +- **ルート定義**: まず軽量な `SKILL.md` で境界と trigger を決めます。 +- **設計**: 適切な archetype、gate、資源の分割を選びます。 +- **実行**: 統一 CLI で作成、検証、最適化、昇格を進めます。 +- **出力**: 最終的に skill package と、評価・ガバナンス・移植性の証拠が残ります。 + +## 加重品質ベンチマーク + +これはプロジェクトのエンジニアリング品質レビューです。各項目を `0-10` で評価し、重みづけして `100` 点満点に換算します。GitHub stars は、エコシステム上の注目度を示すもので、meta-skill の工学的品質を直接示すものではないため、総合点には含めません。 + +加重スコアの式: `sum(score / 10 * weight)`。 + +| Meta Skill | 方法論 15 | 文脈規律 10 | ツールチェーン 15 | Eval/テスト 20 | ガバナンス 15 | 移植性 10 | 導入/レビュー 5 | ローカル信頼性 10 | 加重スコア | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| Yao Meta Skill | 9.5 | 8.0 | 9.5 | 9.5 | 9.5 | 9.0 | 6.5 | 9.5 | 91.5 | +| Anthropic Skill Creator | 9.0 | 6.5 | 8.5 | 7.5 | 4.0 | 5.0 | 7.5 | 5.0 | 67.5 | +| OpenAI Skill Creator | 8.5 | 9.5 | 5.0 | 2.0 | 3.0 | 4.0 | 8.5 | 4.0 | 50.5 | + +| 順位 | Meta Skill | スコア | 中核ポジション | +| ---: | --- | ---: | --- | +| 1 | Yao Meta Skill | 91.5 | 工学化、評価化、ガバナンス化、移植性まで含む完全な meta-skill システム。 | +| 2 | Anthropic Skill Creator | 67.5 | 方法論と反復ループは強いが、ローカル実行信頼性とガバナンス範囲は弱めです。 | +| 3 | OpenAI Skill Creator | 50.5 | 完全な工学システムというより、簡潔な skill 作成ガイドとして有用です。 | + +## 適したシナリオ + +- **チーム再利用、明確な境界、評価ゲート、ガバナンス、移植性、長期保守**を重視するなら `Yao Meta Skill` が向いています。 +- **会話優先の作成ループと人間主導の反復**を重視するなら `Anthropic Skill Creator` が向いています。 +- **簡潔な skill 作成ガイドと文脈規律の見本**が必要なら `OpenAI Skill Creator` が向いています。 +- 実務的な組み合わせとしては、会話的な creator で初稿を作り、その後 `yao-meta-skill` で harden して team-ready な asset にする、という流れが有効です。 + +## クイックスタート + +Codex でこの skill を直接使う場合は、まず global skills にインストールします。 + +```bash +npx -y skills add yaojingang/yao-meta-skill -a codex -g -y +``` + +対応しているすべての agent にインストールする場合は、`-a codex` を `-a '*'` に置き換えます。 + +```bash +npx -y skills add yaojingang/yao-meta-skill -a '*' -g -y +``` + +インストール後にクライアントを再起動してください。その後、"create a skill from this workflow"、"improve this existing skill"、"evaluate this skill"、"add evals to this skill" のような依頼で `yao-meta-skill` を起動できます。 + +1. skill 化したい workflow、prompt 集合、または反復タスクを説明します。 +2. まず短いが人間味のある intent dialogue で、実際の job、outputs、boundary、constraints、重視する品質基準を明確にします。 +3. まず `quickstart` で意図を澄ませ、その後 benchmark scan と reference synthesis を静かに実行します。意図がまだ曖昧なとき、または設計ルートに本当の衝突があるときだけ、追加確認を明示します。 +4. archetype-aware な `quickstart` か完全な authoring flow を使い、scaffold、production、library、governed のいずれかでパッケージを生成または改善します。 +5. 新しく作成した skill には `reports/intent-dialogue.md`、`reports/intent-confidence.md`、`reports/reference-synthesis.md`、`reports/artifact-design-profile.md`、`reports/prompt-quality-profile.md`、`reports/skill-overview.html`、`reports/review-viewer.html`、`reports/iteration-directions.md` が付きます。さらに feedback log と baseline compare を使えば、毎回フル promotion flow を回さずに短い改善ループを回せます。 + +## 現在の結果 + +- 現在 `make test` は通過 +- 現在の回帰セットでは trigger eval が `0` false positives / `0` false negatives +- train / dev / holdout の 3 層評価が通過 +- 中国語の実利用表現も trigger 評価に追加済みです。例: `做一个 skill`、`沉淀成可复用能力`、`优化已有 skill`、`补 trigger 评测` +- `openai`、`claude`、`generic` の packaging contract が通過 + +## 現在の強み + +最新の加重レビューでは、Yao は `91.5/100` です。強みは、長く使うチーム資産としての skill に必要な領域に集中しています。 + +- **方法論の深さ `9.5`**: skill engineering doctrine、archetype、gate selection、non-skill decision、governance、resource boundary が体系化されています。 +- **ツールチェーン完成度 `9.5`**: 初期化、検証、benchmark scan、description optimization、レポート、昇格判定、パッケージ化、CI、portability checks が一つの運用フローに接続されています。 +- **Eval / テスト厳密性 `9.5`**: train/dev/holdout、blind holdout、adversarial holdout、judge-backed blind eval、route confusion、drift history、promotion gate を備えています。 +- **ガバナンス / ライフサイクル `9.5`**: 重要な skill に owner、lifecycle、review cadence、maturity score、trust boundary、promotion decision、regression history を持たせられます。 +- **ローカル実行信頼性 `9.5`**: `make test`、`make ci-test`、統一 CLI でローカル再現できます。 +- **移植性 / 配布 `9.0`**: ソースは中立に保ち、adapter、degradation rule、packaging contract、portability score で環境間の再利用性を担保します。 +- **文脈規律 / 精簡性 `8.0`**: エントリポイントは budget 内に保たれていますが、レポート、例、benchmark、証拠資産が増えたため、継続的な制約として追跡しています。 +- **導入 / レビュー体験 `6.5`**: quickstart、HTML overview、side-by-side review viewer、feedback log により初回体験は改善しましたが、ここは次に最も伸ばすべき UX 領域です。 + +全体の方向性は明確です。入口は軽く、評価は厳しく、ガバナンスは見える形にし、初回作成と人間レビューの摩擦をさらに下げていきます。 + +## なぜ Yao なのか + +- **軽量**: エントリポイントは小さく保たれ、context budget は明示され、追加構造は本当に価値がある場合にだけ導入されます。 +- **厳密**: trigger 品質は family regression、blind holdout、adversarial holdout、route confusion、judge-backed blind eval、promotion gate で検証されます。 +- **ガバナンス可能**: 重要な skill は lifecycle、maturity expectation、owner、review cadence を持つ保守対象の資産として扱われます。 +- **移植可能**: ソースメタデータは中立のまま保たれ、adapter、degradation rule、packaging contract が環境間の再利用意味論を保持します。 + +## 何をするものか + +このプロジェクトは、skill を単発の prompt ではなく、作成・改善・評価・配布できる持続的な能力パッケージとして扱えるようにします。 + +設計ロジックは次の通りです。 + +1. ユーザーの依頼の背後にある反復的な仕事を特定する +2. skill の境界を整理し、1 つのパッケージを 1 つの一貫した役割に保つ +3. 本文を長くする前に trigger description を最適化する +4. メインの skill ファイルを小さく保ち、詳細は references や scripts に移す +5. 品質ゲートは必要なときだけ追加する +6. 本当に必要なクライアント向けにだけ互換出力を生成する + +## なぜ必要か + +多くのチームでは、重要な運用知識が chat、個人 prompt、口頭の習慣、未整理の workflow に散在しています。このプロジェクトは、それらの暗黙知を次の形に変換します。 + +- 発見可能な skill パッケージ +- 再現可能な実行フロー +- 低コンテキストな指示 +- 再利用可能なチーム資産 +- 配布しやすい互換パッケージ + +## リポジトリ構成 + +```text +yao-meta-skill/ +├── SKILL.md +├── README.md +├── LICENSE +├── .gitignore +├── agents/ +│ └── interface.yaml +├── references/ +├── scripts/ +└── templates/ +``` + +## 主要コンポーネント + +### `SKILL.md` + +メインの skill エントリです。トリガー面、動作モード、圧縮された workflow、出力契約を定義します。 + +### `agents/interface.yaml` + +中立的なメタデータの単一ソースです。表示情報と互換性情報を保持し、ソースツリーを特定ベンダーのパスに固定しません。 + +### `references/` + +メイン skill ファイルを肥大化させないための長文資料です。設計ルール、評価方法、互換戦略、品質 rubric を含みます。 + +### `scripts/` + +この meta-skill を実用的にする補助スクリプトです。 + +- `trigger_eval.py`: trigger description が広すぎるか弱すぎるかを確認する +- `context_sizer.py`: コンテキスト量を見積もり、初期ロードが大きすぎる場合に警告する +- `cross_packager.py`: 中立的なソースパッケージからクライアント別出力を生成する + +### `templates/` + +単純な skill と複雑な skill を始めるためのテンプレートです。 + +## 使い方 + +### 1. この skill を直接使う + +次のようなときに `yao-meta-skill` を使います。 + +- 新しい skill を作る +- 既存の skill を改善する +- skill に eval を追加する +- workflow を再利用可能なパッケージにする +- チーム向けに skill を整備する + +### 2. 新しい skill パッケージを生成する + +一般的な流れは次の通りです。 + +1. workflow または能力を説明する +2. trigger フレーズと出力を特定する +3. scaffold、production、library のいずれかを選ぶ +4. パッケージを生成する +5. 必要に応じてサイズチェックと trigger チェックを行う +6. 対象クライアント向けの互換出力を生成する + +### 3. 互換出力を生成する + +例: + +```bash +python3 scripts/cross_packager.py ./yao-meta-skill --platform openai --platform claude --zip +python3 scripts/context_sizer.py ./yao-meta-skill +python3 scripts/trigger_eval.py --description "Create and improve agent skills..." --cases ./cases.json +``` + +## 利点 + +- **prompt ではなく方法論が中心**: skill creation を正式な engineering workflow として扱います +- **トリガー最適化を前提に設計**: description は route confusion、blind holdout、adversarial family、promotion policy で検証されます +- **入口が軽い**: `SKILL.md` は最小限に保ち、references、scripts、evals は必要なときだけ追加します +- **ツールチェーンが一貫**: 初期化、検証、最適化、レポート、パッケージ化、テストを統一 CLI と CI で回せます +- **資産として運用できる**: owner、lifecycle、maturity expectation、review cadence を持つ skill として管理できます +- **移植前提**: ソースは中立、互換性は adapter と degradation rule で処理します +- **証拠が豊富**: route scorecard、regression history、context budget、portability score、promotion decision が公開アーティファクトとして残ります + +## 最適な対象 + +このプロジェクトは次のような人や組織に向いています。 + +- agent builder +- 内部ツールチーム +- prompt engineering から skill engineering に移行したい人 +- 再利用可能な skill ライブラリを構築したい組織 + +## ライセンス + +MIT。詳細は [LICENSE](../LICENSE) を参照してください。 diff --git a/docs/README.ru-RU.md b/docs/README.ru-RU.md new file mode 100644 index 0000000..14cce56 --- /dev/null +++ b/docs/README.ru-RU.md @@ -0,0 +1,240 @@ +# Описание Yao Meta Skill + +`YAO = Yielding AI Outcomes` означает ориентацию на реальные результаты от ИИ. Цель здесь не в том, чтобы написать больше prompt-текста, а в том, чтобы получать переиспользуемые AI-активы и прикладные операционные результаты. + +`yao-meta-skill` — это легкая, но строгая система для создания, оценки, упаковки и управления переиспользуемыми agent skills. + +Он преобразует сырые workflows, transcripts, prompts, notes и runbooks в переиспользуемые skill-пакеты с: + +- понятной поверхностью срабатывания +- компактным `SKILL.md` +- необязательными references, scripts и evals +- более человечным коротким intent dialogue перед глубокой разработкой, с intent confidence gate: если реальная работа, результат, exclusions или стандарты все еще неясны, система продолжает уточнять +- silent-by-default GitHub benchmark scan и reference synthesis до глубокого authoring: система изучает сильные публичные репозитории и world-class pattern tracks, а пользователю явно показывает только реальные конфликты или неопределенность +- явным запросом пользовательских референсов, если они есть, чтобы перенимать паттерны, структуру и стандарты качества, а не копировать формулировки или приватный материал +- автоматически создаваемым минималистичным HTML-обзором на белом фоне для каждого нового skill +- prompt quality profile, который превращает need model, RTF mapping, complexity и prompt-facing quality checks в видимые для reviewer доказательства, не раздувая `SKILL.md` +- тремя наиболее ценными направлениями следующей итерации после первого создания +- компактным HTML review viewer для быстрой первой ручной оценки +- легким feedback log, чтобы не запускать полный promotion flow на каждом цикле +- отчетом with-skill vs baseline для быстрого сравнения инкрементальной пользы +- conversation-style archetype-aware quickstart, который ведет пользователя как discovery flow и помогает выбрать scaffold, production, library или governed форму +- нейтральными исходными метаданными и клиентскими адаптерами +- встроенными проверками governance, promotion и portability в стандартном потоке + +## Архитектура + +В hero-версии вся система сводится к одной линии: превратить разрозненный вход в управляемый и переиспользуемый skill package. + +```mermaid +flowchart LR + A["Входы
workflow / prompt / transcript / docs / notes"] --> B["Маршрут
SKILL.md"] + B --> C["Проектирование
method + gates"] + C --> D["Исполнение
create / validate / eval / promote"] + D --> E["Выходы
skill package + reports + adapters"] +``` + +Как читать это за 10 секунд: + +- **Входы**: стартуем с workflow, prompt, документов и заметок. +- **Маршрут**: компактный `SKILL.md` сначала задает границу и trigger. +- **Проектирование**: выбираются нужные archetype, quality gates и разбиение ресурсов. +- **Исполнение**: единый CLI создает, проверяет, оптимизирует и продвигает skill. +- **Выходы**: результатом становится skill package плюс доказательства оценки, governance и portability. + +## Взвешенный quality benchmark + +Это инженерная оценка проекта. Каждое измерение оценивается по шкале `0-10`, затем пересчитывается в `100` баллов с учетом веса. GitHub stars не включаются, потому что они отражают популярность экосистемы, а не инженерное качество meta-skill. + +Формула взвешенного балла: `sum(score / 10 * weight)`. + +| Meta Skill | Методология 15 | Context discipline 10 | Toolchain 15 | Eval/tests 20 | Governance 15 | Portability 10 | Onboarding/review 5 | Local reliability 10 | Weighted score | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| Yao Meta Skill | 9.5 | 8.0 | 9.5 | 9.5 | 9.5 | 9.0 | 6.5 | 9.5 | 91.5 | +| Anthropic Skill Creator | 9.0 | 6.5 | 8.5 | 7.5 | 4.0 | 5.0 | 7.5 | 5.0 | 67.5 | +| OpenAI Skill Creator | 8.5 | 9.5 | 5.0 | 2.0 | 3.0 | 4.0 | 8.5 | 4.0 | 50.5 | + +| Место | Meta Skill | Балл | Основное позиционирование | +| ---: | --- | ---: | --- | +| 1 | Yao Meta Skill | 91.5 | Полная система engineering, evaluation, governance и portability для переиспользуемых skills. | +| 2 | Anthropic Skill Creator | 67.5 | Сильная методология и итерационный цикл, но слабее local execution reliability и governance coverage. | +| 3 | OpenAI Skill Creator | 50.5 | Лучше как краткое руководство по написанию skills, чем как полноценная engineering system. | + +## Подходящие сценарии + +- Выбирайте **Yao Meta Skill**, если нужен командный переиспользуемый актив с явными границами, evaluation gates, governance, portability и долгосрочной поддержкой. +- Выбирайте **Anthropic Skill Creator**, если вам важнее conversation-first creation loop и итерация с сильным human guidance. +- Выбирайте **OpenAI Skill Creator**, если вам нужен краткий reference по написанию lean skills и context discipline. +- Практичный гибридный вариант: сначала получить первый черновик через conversation-driven creator, а затем использовать `yao-meta-skill`, чтобы усилить пакет и сделать его team-ready. + +## Быстрый старт + +Чтобы использовать эту skill напрямую в Codex, сначала установите ее в global skills: + +```bash +npx -y skills add yaojingang/yao-meta-skill -a codex -g -y +``` + +Чтобы установить ее во все поддерживаемые agents, замените `-a codex` на `-a '*'`: + +```bash +npx -y skills add yaojingang/yao-meta-skill -a '*' -g -y +``` + +После установки перезапустите клиент. Затем используйте запросы вроде "create a skill from this workflow", "improve this existing skill", "evaluate this skill" или "add evals to this skill", чтобы вызвать `yao-meta-skill`. + +1. Опишите workflow, набор prompts или повторяющуюся задачу, которую хотите превратить в skill. +2. Сначала проведите короткий, но более человечный intent dialogue, чтобы уточнить реальную job-to-be-done, outputs, exclusions, constraints и те стандарты качества, которые для вас важны. +3. Сначала позвольте `quickstart` прояснить намерение, затем тихо выполнить benchmark scan и reference synthesis. Явные уточнения поднимаются только тогда, когда intent все еще неясен или между маршрутами проектирования есть реальный конфликт. +4. Используйте archetype-aware `quickstart` или полный authoring flow, чтобы сгенерировать или улучшить пакет в режиме scaffold, production, library или governed. +5. Каждый новый skill также получает `reports/intent-dialogue.md`, `reports/intent-confidence.md`, `reports/reference-synthesis.md`, `reports/artifact-design-profile.md`, `reports/prompt-quality-profile.md`, `reports/skill-overview.html`, `reports/review-viewer.html` и `reports/iteration-directions.md`. После этого feedback log и baseline compare позволяют запускать короткий цикл улучшений без полного promotion flow. + +## Текущие результаты + +- `make test` сейчас проходит +- на текущем regression-наборе trigger eval дает `0` false positives и `0` false negatives +- все три набора train / dev / holdout проходят +- реальные китайские формулировки теперь включены в trigger eval, например `做一个 skill`, `沉淀成可复用能力`, `优化已有 skill`, `补 trigger 评测` +- packaging contracts для `openai`, `claude` и `generic` проходят проверку + +## Текущие сильные стороны + +В последней взвешенной оценке Yao набирает `91.5/100`. Его сильные стороны сосредоточены в том, что нужно для долговечных командных skill-активов: + +- **Глубина методологии `9.5`**: формальная skill engineering doctrine, archetypes, gate selection, non-skill decisions, governance и resource boundaries. +- **Полнота toolchain `9.5`**: initialization, validation, benchmark scan, description optimization, reporting, promotion checks, packaging, CI и portability checks связаны в один operational flow. +- **Строгость Eval / tests `9.5`**: train/dev/holdout, blind holdout, adversarial holdout, judge-backed blind eval, route confusion, drift history и promotion gates покрыты. +- **Governance / lifecycle `9.5`**: важные skills могут иметь owner, lifecycle, review cadence, maturity score, trust boundary, promotion decision и regression history. +- **Local execution reliability `9.5`**: репозиторий проверяется локально через `make test`, `make ci-test` и unified CLI. +- **Portability / distribution `9.0`**: neutral metadata, adapters, degradation rules, packaging contracts и portability score сохраняют переносимую семантику между средами. +- **Context discipline `8.0`**: entrypoint остается в рамках budget, но эта метрика отслеживается как живое ограничение, потому что система несет больше reports, examples, benchmark assets и evidence. +- **Onboarding / review experience `6.5`**: quickstart, HTML overview, side-by-side review viewer и feedback log улучшили первый опыт, но это все еще главный UX-направление для улучшения. + +Общий вектор здесь осознанный: легкий вход, сложная для имитации evaluation, видимая governance и снижение трения первого создания и ручного review. + +## Почему Yao + +- **Легкий**: entrypoint остается компактным, context budgets заданы явно, а дополнительная структура добавляется только тогда, когда она действительно окупается. +- **Строгий**: качество trigger проверяется через family regressions, blind holdout, adversarial holdout, route confusion, judge-backed blind eval и promotion gates. +- **Управляемый**: важные skills рассматриваются как поддерживаемые активы с lifecycle, maturity expectation, owner и review cadence. +- **Портируемый**: source metadata остается нейтральной, а adapters, degradation rules и packaging contracts сохраняют переносимую семантику между средами. + +## Что делает проект + +Этот проект помогает создавать, перерабатывать, оценивать и упаковывать skills как долговечные capability-пакеты, а не как одноразовые prompts. + +Его логика проста: + +1. определить реальную повторяющуюся задачу за пользовательским запросом +2. задать чистую границу skill, чтобы один пакет решал одну связанную задачу +3. оптимизировать trigger description до того, как раздувать основное тело +4. держать основной файл маленьким, а детали переносить в references или scripts +5. добавлять quality gates только тогда, когда они действительно окупаются +6. экспортировать compatibility artifacts только для реально нужных клиентов + +## Зачем нужен этот проект + +У большинства команд важные операционные знания разбросаны по чатам, личным prompts, устным привычкам и недокументированным workflows. Этот проект превращает такое скрытое знание в: + +- обнаруживаемые skill-пакеты +- повторяемые execution flows +- инструкции с меньшей нагрузкой на контекст +- переиспользуемые командные активы +- готовые к совместимости дистрибутивы + +## Структура репозитория + +```text +yao-meta-skill/ +├── SKILL.md +├── README.md +├── LICENSE +├── .gitignore +├── agents/ +│ └── interface.yaml +├── references/ +├── scripts/ +└── templates/ +``` + +## Ключевые компоненты + +### `SKILL.md` + +Главная точка входа skill. Здесь задаются surface trigger, operating modes, compact workflow и output contract. + +### `agents/interface.yaml` + +Нейтральный единый источник метаданных. Он хранит display и compatibility metadata, не привязывая дерево исходников к vendor-specific path. + +### `references/` + +Длинные материалы, которые не должны раздувать основной skill-файл. Здесь находятся design rules, evaluation guidance, compatibility strategy и quality rubrics. + +### `scripts/` + +Утилиты, которые делают meta-skill по-настоящему рабочей: + +- `trigger_eval.py`: проверяет, не слишком ли широкая или слабая trigger description +- `context_sizer.py`: оценивает вес контекста и предупреждает, если initial load становится слишком большим +- `cross_packager.py`: собирает client-specific export artifacts из нейтрального исходного пакета + +### `templates/` + +Стартовые шаблоны для простых и более сложных skill-пакетов. + +## Как использовать + +### 1. Использовать skill напрямую + +Вызывайте `yao-meta-skill`, когда хотите: + +- создать новую skill +- улучшить существующую skill +- добавить evals в skill +- превратить workflow в переиспользуемый пакет +- подготовить skill для более широкого использования в команде + +### 2. Сгенерировать новый skill-пакет + +Типичный поток: + +1. описать workflow или capability +2. определить trigger phrases и expected outputs +3. выбрать режим scaffold, production или library +4. сгенерировать пакет +5. при необходимости запустить size и trigger checks +6. экспортировать targeted compatibility artifacts + +### 3. Экспортировать compatibility artifacts + +Примеры: + +```bash +python3 scripts/cross_packager.py ./yao-meta-skill --platform openai --platform claude --zip +python3 scripts/context_sizer.py ./yao-meta-skill +python3 scripts/trigger_eval.py --description "Create and improve agent skills..." --cases ./cases.json +``` + +## Преимущества + +- **Сначала метод, а не просто prompt**: skill creation рассматривается как формальный engineering workflow +- **Ориентация на trigger optimization**: description проходит через route confusion, blind holdout, adversarial families и promotion policy +- **Легкий entrypoint**: `SKILL.md` остается компактным, а references, scripts и evals добавляются только когда действительно нужны +- **Цельная toolchain**: init, validate, optimize, report, package и test проходят через единый CLI и CI path +- **Управляется как актив**: важные skills могут иметь owner, lifecycle, maturity expectation и review cadence +- **Портируемость по умолчанию**: исходники нейтральны, а совместимость обрабатывается через adapters и degradation rules +- **Высокая плотность доказательств**: route scorecards, regression history, context budgets, portability scores и promotion decisions публикуются как артефакты + +## Для кого подходит + +Проект лучше всего подходит для: + +- agent builders +- команд внутреннего tooling +- prompt engineers, переходящих к skill engineering +- организаций, создающих библиотеки переиспользуемых skills + +## Лицензия + +MIT. См. [LICENSE](../LICENSE). diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md new file mode 100644 index 0000000..ae65c2d --- /dev/null +++ b/docs/README.zh-CN.md @@ -0,0 +1,333 @@ +# Yao Meta Skill 中文介绍 + +`YAO = Yielding AI Outcomes`,中文可理解为:产出 AI 结果,交付真实成果。它强调的不是生成更多 prompt 文本,而是沉淀可复用的 AI 资产与可落地的实际结果。 + +`yao-meta-skill` 用来创建、评估、打包和治理可复用的 agent skill。1.0 的重点是把重复工作流整理成可安装、可阅读、可跨平台的 skill 包;2.0 进一步升级为 Skill OS,把建模、跨端编译、输出评测、评审工作台、证据账本、包体验证、发布门禁和后续迭代串成一套完整流程。 + +[快速开始](#快速开始) · [Skill OS 2.0 升级](#skill-os-20-升级) · [从 1.0 到 2.0](#从-10-到-20) · [Operator UX 命令](#operator-ux-命令) · [加权质量评测](#加权质量评测) · [与其他元 Skill 的适用差异](#与其他元-skill-的适用差异) + +## Skill OS 2.0 升级 + +Skill OS 2.0 保留 `yao-meta-skill` 原来的轻量入口,但把 skill 的生命周期说得更清楚。它不止生成 `SKILL.md`,还会围绕这个 skill 生成结构化契约、目标平台适配、评测证据、发布门禁和运营报告。 + +- **Skill IR(技能中间表示)**:用平台无关的结构记录意图、触发方式、输入输出、边界、参考资料和交付产物。 +- **目标编译与适配器**:把同一份 skill 语义编译到 OpenAI、Claude、通用 agent skill、Agent Skills 兼容包和 VS Code 使用场景。 +- **输出评测实验室**:覆盖触发评测、输出断言、执行证据、耗时与 token 证据、基准复现、盲评材料包、答案钥匙和评审裁决报告。 +- **Review Studio 2.0(评审工作台)**:把意图、触发、输出评测、上下文成本、运行时检查、信任扫描、Skill Atlas、采用漂移、豁免、批注、发布证据、警告、阻塞项和修复动作放在同一个 HTML 页面。 +- **证据与发布治理**:提供证据一致性、包体验证、安装模拟、运行时权限探测、世界级证据接收、证据账本、操作运行手册和公开宣传守卫。 +- **SkillOps 闭环**:通过元数据级采用漂移、遥测 hook、自适应建议、日报/周报和组合级漂移信号,把发布后的反馈带回下一轮迭代。 + +当前发布口径:仓库已经适合进入测试版和外部试用,但更强的“世界级”公开宣传仍然受证据门禁约束。真实生产模型证据、真人盲评证据、原生权限执行和真实客户端遥测会继续作为独立证据任务推进,不会被包装成已经完成。 + +相关产物: + +- [1.0 vs 2.0 可视化对比报告](../.previews/yao-meta-skill-2-comparison/index.html) +- [中文桌面预览图](../.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2.png) +- [英文桌面预览图](../.previews/yao-meta-skill-2-comparison/yao-meta-skill-1-vs-2-en.png) + +## 从 1.0 到 2.0 + +| 维度 | 1.0 重点 | 2.0 升级 | +| --- | --- | --- | +| 产品定位 | 创建、重构、评估和打包可复用 skill。 | 治理 skill 的完整生命周期:创建、编译、评测、评审、发布、遥测和迭代。 | +| 架构模型 | 以 `SKILL.md`、`agents/interface.yaml`、manifest 和报告文件为主。 | 引入 Skill IR、目标编译器、适配器、门禁契约、证据账本、发布锁和行动型评审页面。 | +| 跨端交付 | 主要覆盖 OpenAI、Claude 和通用包体。 | 扩展到 Agent Skills 兼容包和 VS Code 使用场景,并把兼容结果写入 registry 可读记录。 | +| 质量模型 | 触发、结构和报告式评审。 | 输出评测、基准复现、执行证据、失败披露、盲评材料包和证据一致性检查。 | +| 报告体验 | 概览 HTML 和首次评审页面。 | 双语 Skill Overview v2、Review Studio 2.0、评审批注、行动卡片、图表和审计型报告契约。 | +| 发布边界 | 能生成包体并做基础校验。 | 包体验证、安装模拟、运行时权限探测、发布锁、公开宣传守卫和操作运行手册。 | +| 运营闭环 | 更多依赖手动反馈和本地迭代。 | 采用漂移、元数据遥测、SkillOps 报告、自适应建议和组合级漂移检测。 | + +## 2.0 使用场景 + +- **从重复工作创建新 skill**:从 workflow 笔记、prompt 集合、转录、runbook 或文档模式出发,生成精简入口、明确输入输出、参考资料、报告和最轻量的必要门禁。 +- **把个人 skill 升级成团队资产**:补齐接口契约、manifest、目标适配器、信任检查、输出评测、评审豁免、发布说明和 Review Studio 证据,再交给多人复用。 +- **准备测试版发布**:运行包体验证、安装模拟、兼容检查、运行时权限探测和证据一致性检查,把测试版就绪和更强公开声明分开处理。 +- **发布后继续迭代**:用元数据级遥测、采用漂移、feedback log、SkillOps 报告和自适应建议判断下一步是补文档、补 eval、改 skill,还是调整治理规则。 +- **与其他元 skill 搭配使用**:保留 Anthropic/OpenAI 式对话创建和精简写作方法的优势,在需要证据、可移植性、发布门禁和长期维护时用 `yao-meta-skill` 加固。 + +## Operator UX 命令 + +这组只读辅助命令把维护者最常见的判断变成可复验诊断: + +```bash +python3 scripts/yao.py install-status --expected-source . +python3 scripts/yao.py localized-doc-sync-check +python3 scripts/yao.py pr-review-report 4 --repo yaojingang/yao-meta-skill +``` + +- `install-status` 会说明当前 skill 是从 `.codex/skills`、`.agents/skills` 还是 disabled mirror(禁用镜像)被发现,并提示是否存在重复激活。 +- `localized-doc-sync-check` 会检查中文 README 是否同步包含英文 README 首页新增的公开说明模块。 +- `pr-review-report` 会只读获取 GitHub PR 元数据、变更文件、状态检查和建议本地命令,不会合并或修改 PR。 + +## 能力面 + +它把粗糙的 workflow、transcript、prompt、notes 和 runbook 转成可复用的 skill 包,并具备: + +- 清晰的触发面 +- 精简的 `SKILL.md` +- 可选的 references、scripts 和 evals +- 深度起草前先做一轮更有人味的意图对话,并通过 intent confidence gate 判断理解是否足够清楚;如果不够清楚,会继续补 1 到 2 个高杠杆问题 +- 深度起草前会静默执行 GitHub benchmark scan 和 reference synthesis,优先学习高质量公开项目与世界级模式;只有遇到真实冲突或不确定性时才显式抬给用户 +- 会主动询问用户是否有希望借鉴的参考对象,只学习其中的模式抽象、结构和标准,不复制原文或私密内容 +- 新建 skill 时自动生成一份默认中文、可切换英文的 HTML 可视化说明报告 +- 提供 Review Studio 2.0,把意图、触发、输出评测、上下文、运行时、信任、采用漂移、豁免、批注、发布证据和修复动作汇总到一个页面 +- 提供 Skill OS 2.0 审计,把世界级要求拆成本地证据、人类证据缺口和外部证据缺口 +- 提供证据计划、证据账本、证据接收契约、提交审查队列、操作运行手册和公开宣传守卫,避免把计划中的工作写成已经完成 +- 提供基准复现 manifest、证据一致性检查、输出评测实验室、盲评包和评审裁决报告 +- 提供运行时权限探测、Python 兼容性门禁、包体验证和安装模拟 +- 提供 prompt quality profile,把需求模型、RTF 映射、复杂度和提示词质量检查沉淀成 reviewer 可见证据,而不是塞进 `SKILL.md` +- 提供 artifact design profile,约束报告、教程、仪表盘、截图和评审页的视觉与质量标准 +- 提供系统思考模型,记录边界、反馈环、漂移风险、复发失败模式和高杠杆改进点 +- 首次建包后会自动给出 3 个最有价值的下一步迭代方向 +- 提供一个紧凑的 HTML review viewer,方便第一次人工理解和评审 +- 提供一个轻量 feedback log,不必每次都走完整 promotion 流程 +- 提供本地优先、只记录元数据的 adoption drift 报告、CLI 运行捕获、外部客户端事件 hook、hook recipe 和 JSONL 导入 +- 提供显式来源的 adaptive proposal loop,从脱敏重复偏好里生成需要人工批准的改进建议 +- 提供 SkillOps 机会评分、决策策略、日报、周报和组合级漂移信号 +- 提供一个 with-skill vs baseline 的对比报告,便于快速看增量收益 +- 提供一个更像对话发现流程的 archetype-aware quickstart,引导新 skill 落到 scaffold、production、library 或 governed 的合适形态 +- Skill IR 作为平台无关语义契约,并提供编译报告和客户端适配层 +- registry audit 元数据,包含 package version、owner、license、checksum 和 compatibility matrix +- 内建的治理、晋升和 portability 检查 + +## 架构图 + +Hero 版可以压缩成一条主线:Skill OS 2.0 把零散输入变成一个可治理、可评测、可发布、可持续迭代的 skill 包。 + +```mermaid +flowchart LR + A["输入
workflow / prompt / transcript / docs / notes"] --> B["意图建模
任务 / 输出 / 排除项 / 标准"] + B --> C["Skill IR
触发 / 契约 / 资源 / 证据"] + C --> D["Skill 包
SKILL.md / references / scripts / reports"] + C --> E["目标编译
OpenAI / Claude / generic / Agent Skills / VS Code"] + D --> F["评测实验
触发 / 输出 / 基准 / 运行时"] + E --> F + F --> G["Review Studio
门禁 / 警告 / 动作 / 豁免"] + G --> H["发布边界
包体验证 / 安装模拟 / 宣传守卫"] + H --> I["SkillOps 闭环
反馈 / 采用漂移 / 下一轮迭代"] + I --> B +``` + +10 秒理解这张图: + +- **输入**:从零散 workflow、prompt、文档和笔记出发,不要求一开始就有完整规格。 +- **意图建模**:先把任务、输出物、排除项、约束和质量标准说清楚,再生成文件。 +- **Skill IR**:把语义契约和具体平台格式拆开,避免被某一个客户端锁死。 +- **打包与编译**:从同一份源模型生成精简 skill 包和目标平台适配产物。 +- **评测与评审**:把触发行为、输出质量、运行时检查和信任信号变成可复查证据。 +- **发布与运营**:只在当前证据边界内发布,再把采用漂移和评审反馈带回下一轮迭代。 + +## 加权质量评测 + +下面是当前项目采用的工程质量评测模型。每个维度按 `0-10` 评分,再按权重折算到 `100` 分。GitHub stars 不计入总分,因为它反映生态热度,不直接代表元 skill 工程质量。 + +这个分数是本地工程证据,不等同于 world-class ready。公开宣称“已证明优于其他方案”仍要以 world-class ledger 中已接受的外部证据和人工证据为准。 + +加权总分公式:`sum(单项评分 / 10 * 权重)`。 + +| 元 Skill | 方法论深度 15 | 上下文纪律 10 | 工具链 15 | Eval/测试 20 | 治理 15 | 可移植 10 | 上手/评审 5 | 本地可靠性 10 | 加权总分 | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| Yao Meta Skill | 9.5 | 8.0 | 9.5 | 9.5 | 9.5 | 9.0 | 6.5 | 9.5 | 91.5 | +| Anthropic Skill Creator | 9.0 | 6.5 | 8.5 | 7.5 | 4.0 | 5.0 | 7.5 | 5.0 | 67.5 | +| OpenAI Skill Creator | 8.5 | 9.5 | 5.0 | 2.0 | 3.0 | 4.0 | 8.5 | 4.0 | 50.5 | + +| 排名 | 元 Skill | 总分 | 核心定位 | +| ---: | --- | ---: | --- | +| 1 | Yao Meta Skill | 91.5 | 工程化、评测化、治理化、可移植的完整元 skill 系统。 | +| 2 | Anthropic Skill Creator | 67.5 | 方法论和迭代闭环强,但本地执行可靠性和治理覆盖较弱。 | +| 3 | OpenAI Skill Creator | 50.5 | 更适合作为精简 skill 写作方法论教材,而不是完整工程系统。 | + +## 人工盲测快照 + +2026-06-29,一位人工评审者在 5 个真实常见的 skill 创建场景里,对比了 `yao-meta-skill` 和内置的 OpenAI `skill-creator`。5 个场景分别是客服工单分诊、月度收入对账、Webinar 内容复用、故障复盘和 PR Review 跟进。评审者确认:所有选择都在揭晓来源前完成。 + +结果:`yao-meta-skill` 在 `5/5` 个案例中胜出。 + +证据: + +- 盲测入口:[reports/blind-human-review-2026-06-29/index.html](../reports/blind-human-review-2026-06-29/index.html) +- Adjudication 摘要:[reports/blind-human-review-2026-06-29/adjudication.md](../reports/blind-human-review-2026-06-29/adjudication.md) +- 已记录判断:[reports/blind-human-review-2026-06-29/review-decisions.recorded.json](../reports/blind-human-review-2026-06-29/review-decisions.recorded.json) + +边界:这是单人盲测偏好证据,不是 provider-backed 的独立模型执行证据;每个案例的逐项理由仍为空。 + +## 与其他元 Skill 的适用差异 + +- 如果你要的是**团队复用、显式边界、质量门、治理、可移植性和长期维护**,更适合 `Yao Meta Skill`。 +- 如果你要的是**对话优先的创作循环和人工引导式迭代**,更适合 `Anthropic Skill Creator`。 +- 如果你要的是**精简的 skill 写作参考和上下文纪律示范**,更适合 `OpenAI Skill Creator`。 +- 一个很实用的组合方式是:先用更对话式的系统做第一版,再用 `yao-meta-skill` 把它加固成团队可复用的正式资产。 + +## 快速开始 + +如果你想直接在 Codex 里使用这个 skill,先安装到全局 skills: + +```bash +npx -y skills add yaojingang/yao-meta-skill -a codex -g -y +``` + +如果要安装到全部支持的 agent,把 `-a codex` 换成 `-a '*'`: + +```bash +npx -y skills add yaojingang/yao-meta-skill -a '*' -g -y +``` + +安装完成后重启客户端,再用“创建 skill”“改进已有 skill”“评估 skill”“给 skill 增加 eval”这类任务触发 `yao-meta-skill`。 + +1. 先描述你想沉淀成 skill 的 workflow、prompt 集合或重复任务。 +2. 先做一轮简短但更有人味的意图对话,把真实任务、输出物、边界、约束和你在意的质量标准说清楚。 +3. 先让 `quickstart` 澄清意图,再静默跑 benchmark scan 和 reference synthesis;只有当意图还不清楚,或者设计路线真的冲突时,才会显式继续追问或让你拍板。 +4. 使用 archetype-aware 的 `quickstart` 或完整作者流,在 scaffold、production、library 或 governed 模式下生成或改进 skill 包。 +5. 新建 skill 后,先看 `reports/skill-interpretation.html` 理解双语解释报告,再打开 `reports/skill-overview.html` 查看概述、指标、原理、边界、质量、风险、资产和路线,最后用 `reports/review-studio.html` 检查发布阻塞、权限批准和证据路径。 + +## 当前结果 + +- 当前 `make test` 和 GitHub Actions `test` 可通过 +- 当前回归集下 trigger eval 为 `0` 误触发、`0` 漏触发 +- train / dev / holdout 三层评测均通过 +- 中文真实表达已经纳入触发评测,覆盖“做一个 skill”“沉淀成可复用能力”“优化已有 skill”“补 trigger 评测”等常见说法 +- registry 记录的目标平台从 OpenAI、Claude、Generic 扩展到 Agent Skills 和 VS Code 相关适配 +- Review Studio 当前汇总 16 个门禁,包体验证、安装模拟、证据一致性和发布声明边界已经进入报告链路 +- 单人盲测快照中,评审者在揭晓来源前完成判断,并在 `5/5` 个真实 skill 创建场景中选择 `yao-meta-skill`;证据见 [reports/blind-human-review-2026-06-29/adjudication.md](../reports/blind-human-review-2026-06-29/adjudication.md) + +## 当前优势 + +最新加权评测中,Yao 的总分是 `91.5/100`。2.0 的强项集中在团队级 skill 资产真正需要的能力上: + +- **方法论深度 `9.5`**:已经形成正式的 skill engineering doctrine,覆盖 archetype、gate selection、non-skill decision、governance 和 resource boundary。 +- **工具链完整度 `9.5`**:初始化、校验、benchmark scan、description optimization、Skill IR、目标编译、报告、晋升检查、打包、CI 和 portability checks 已经串成一条完整工具链。 +- **Eval / 测试严谨度 `9.5`**:触发评测覆盖 train/dev/holdout、blind holdout、adversarial holdout、judge-backed blind eval、route confusion、drift history、output eval 和 promotion gate。 +- **治理 / 生命周期 `9.5`**:重要 skill 可以声明 owner、生命周期、review cadence、maturity score、trust boundary、promotion decision、regression history、release lock 和 evidence ledger。 +- **本地可执行可靠性 `9.5`**:可以通过 `make test`、`make ci-test` 和统一 CLI 在本地复验。 +- **可移植 / 分发能力 `9.0`**:源码保持中性,adapter、degradation rule、packaging contract 和 portability score 负责保留跨环境可复用语义。 +- **上下文纪律 / 精简度 `8.0`**:入口仍保持在预算内,但因为系统承载了更多报告、案例、benchmark 和证据资产,这一项被持续作为约束跟踪。 +- **上手 / 评审体验 `6.5`**:quickstart、HTML overview、side-by-side review viewer 和 feedback log 已经改善首次体验,但仍是下一阶段最值得优化的 UX 维度。 + +整体方向很明确:入口尽量轻,评测尽量硬,治理显性化;测试版可以开放试用,但公开宣传会继续受证据账本约束。 + +## 为什么是 Yao + +- **轻量**:入口保持紧凑,context budget 明确分层,只有在真正值得时才增加额外结构。 +- **严谨**:trigger 质量会经过 family regression、blind holdout、adversarial holdout、route confusion、judge-backed blind eval 和 promotion gate 的联合检查。 +- **可治理**:重要 skill 被当成可维护资产处理,具备 lifecycle、maturity expectation、owner 和 review cadence。 +- **可移植**:源码元数据保持中性,adapter、degradation rule 和 packaging contract 负责保留跨环境可复用语义。 + +## 它能做什么 + +这个项目帮助你把 skill 从一次性 prompt,升级成可创建、可重构、可评估、可打包、可审计、可发布、可持续迭代的长期能力包。 + +它的设计逻辑很简单: + +1. 识别用户请求背后真正重复发生的工作 +2. 划清 skill 边界,让一个包只做一个连贯的任务 +3. 优先优化触发 description,而不是先把正文写长 +4. 保持主 skill 文件精简,把细节移到 references 或 scripts +5. 只在值得时加入质量门槛 +6. 只为真正需要的客户端导出兼容产物 + +## 为什么要做它 + +大多数团队的重要操作知识都散落在聊天记录、个人 prompt、口头习惯和未成文 workflow 中。这个项目的作用,是把这些隐性流程知识转成: + +- 可发现的 skill 包 +- 可重复的执行流程 +- 更低上下文负担的指令 +- 可复用的团队资产 +- 可兼容分发的产物 + +## 仓库结构 + +```text +yao-meta-skill/ +├── SKILL.md +├── README.md +├── LICENSE +├── .gitignore +├── agents/ +│ └── interface.yaml +├── references/ +├── scripts/ +└── templates/ +``` + +## 核心组成 + +### `SKILL.md` + +主 skill 入口,定义触发面、工作模式、压缩后的工作流和输出契约。 + +### `agents/interface.yaml` + +中性的元数据单一来源。它保存显示信息和兼容性信息,不把源码树锁定到某一家厂商的专属路径。 + +### `references/` + +用于存放不应该塞进主 skill 文件的长文档,包括设计规则、评估方法、兼容策略和质量 rubric。 + +### `scripts/` + +让这个元 skill 具备工程化能力的辅助脚本: + +- `trigger_eval.py`:检查 trigger description 是否过宽或过弱 +- `context_sizer.py`:估算上下文体积,并在初始加载过大时给出警告 +- `cross_packager.py`:从中性的源码包生成客户端特定的导出产物 + +### `templates/` + +用于生成简单 skill 和更复杂 skill 的起步模板。 + +## 如何使用 + +### 1. 直接使用这个 skill + +当你想做以下事情时,可以调用 `yao-meta-skill`: + +- 创建新 skill +- 改进已有 skill +- 给 skill 增加 eval +- 把 workflow 变成可复用包 +- 为更广泛的团队使用准备 skill + +### 2. 生成一个新的 skill 包 + +典型流程是: + +1. 描述 workflow 或能力 +2. 识别触发语句和目标输出 +3. 选择 scaffold、production 或 library 模式 +4. 生成 skill 包 +5. 在需要时运行体积检查和触发检查 +6. 导出面向目标客户端的兼容产物 + +### 3. 导出兼容产物 + +示例: + +```bash +python3 scripts/cross_packager.py ./yao-meta-skill --platform openai --platform claude --zip +python3 scripts/context_sizer.py ./yao-meta-skill +python3 scripts/trigger_eval.py --description "Create and improve agent skills..." --cases ./cases.json +``` + +## 优势 + +- **方法论优先,不是 prompt 优先**:skill creation 被当成正式工程流程,而不是只写一段说明文字 +- **天生面向触发优化**:description 会经过 route confusion、blind holdout、adversarial family 和 promotion policy 的检查 +- **入口轻量**:`SKILL.md` 保持克制,references、scripts、evals 只在真正值得时加入 +- **工具链完整**:初始化、校验、优化、报告、打包、测试,都能走统一 CLI 和 CI 路径 +- **治理化资产**:重要 skill 可以带 owner、lifecycle、maturity expectation 和 review cadence +- **默认可移植**:源码中立,兼容性通过 adapter 和 degradation rule 处理 +- **证据密度高**:route scorecard、regression history、context budget、portability score、promotion decision 都是公开产物,而不是隐藏实现 + +## 最适合谁 + +这个项目尤其适合: + +- agent 构建者 +- 内部工具团队 +- 正在从 prompt engineering 转向 skill engineering 的人 +- 想构建可复用 skill 库的组织 + +## 许可证 + +MIT。见 [LICENSE](../LICENSE)。 diff --git a/docs/assets/social-preview.png b/docs/assets/social-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..3d65afa532ac464e2c2c19fe8435b3726c6081e7 GIT binary patch literal 48266 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 R(?9J4repX=+3ow!{vDHc%b@@O literal 0 HcmV?d00001 diff --git a/docs/assets/social-preview.svg b/docs/assets/social-preview.svg new file mode 100644 index 0000000..efcda6b --- /dev/null +++ b/docs/assets/social-preview.svg @@ -0,0 +1,33 @@ + + + YAO + Yielding AI Outcomes + A rigorous engineering, evaluation, governance, and portability system + for reusable agent skills. + + + Weighted Score + 91.5 + / 100 + Method 9.5 · Eval 9.5 · Governance 9.5 + + + Engineering + Unified CLI, CI, validation, reporting, and packaging. + + + Evaluation + Holdouts, route confusion, judge-backed blind evals. + + + Governance + Lifecycle, maturity, promotion, and review evidence. + + + Portability + Neutral metadata, adapters, contracts, and degradation. + + + + Reusable agent skills with explicit boundaries, strict evaluation, visible governance, and local reliability. + diff --git a/docs/migration-v2.md b/docs/migration-v2.md new file mode 100644 index 0000000..7189997 --- /dev/null +++ b/docs/migration-v2.md @@ -0,0 +1,63 @@ +# Migration To Skill OS 2.0 + +This document tracks the first migration path from Yao Meta Skill 1.x to the Skill OS 2.0 architecture. + +## 1.x To 2.0 Mapping + +| 1.x Asset | 2.0 Role | +| --- | --- | +| `SKILL.md` frontmatter | `skill-ir.trigger_surface.description` | +| `reports/intent-context.json` | `skill-ir.job_to_be_done` and input/output assumptions | +| `evals/trigger_cases.json` | `skill-ir.trigger_surface` samples and Trigger Eval Lab | +| `reports/baseline-compare.*` | legacy baseline evidence for Output Eval Lab | +| `reports/output-risk-profile.*` | failure taxonomy seed for Output Eval Lab | +| `agents/interface.yaml` | target compatibility seed for runtime conformance | +| `manifest.json` | governance and registry metadata seed | + +## Migration Steps + +1. Export Skill IR: + + ```bash + python3 scripts/export_skill_ir.py . --output-json skill-ir/examples/yao-meta-skill.json + ``` + +2. Run Output Eval Lab: + + ```bash + python3 scripts/run_output_eval.py + ``` + +3. Run runtime conformance and trust checks: + + ```bash + python3 scripts/run_conformance_suite.py . + python3 scripts/trust_check.py . + ``` + +4. For library or team workspaces, build the Skill Atlas: + + ```bash + python3 scripts/build_skill_atlas.py --workspace-root . --output-dir skill_atlas --report-html reports/skill_atlas.html --report-json reports/skill_atlas.json + ``` + + This also writes `skill_atlas/drift_signals.json` from aggregate `reports/adoption_drift_report.json` files. Do not migrate or publish raw `reports/telemetry_events.jsonl` logs. + +5. Keep legacy trigger eval gates intact while output eval coverage grows. + +6. Move adapter-specific logic toward future compiler commands after IR fields are stable. + +7. Treat new 2.0 checks as tiered gates: scaffold stays light, production adds output eval, library adds conformance and atlas, governed adds trust and release evidence. + +## Compatibility Strategy + +No existing 1.x command is removed in the v0 migration. New commands produce additional artifacts and should become release gates only after their cases and schemas stabilize. + +## Deprecation Notes + +- Legacy baseline compare remains useful but should not be treated as full output quality proof. +- Platform packaging should continue to work directly until compiler-based packaging is introduced. +- Review Studio 2.0 should aggregate existing reports before replacing any current report. +- Conformance v0 is a structural and metadata gate; it does not yet simulate each client runtime. +- Trust v0 blocks obvious secret and remote-inline-execution risks, while surfacing script and dependency warnings for reviewer judgment. +- Skill Atlas v0 is local-first portfolio analysis; avoid committing reports generated from private parent workspaces unless the names and routes are intended to be shared. diff --git a/evals/README.md b/evals/README.md new file mode 100644 index 0000000..e1e848e --- /dev/null +++ b/evals/README.md @@ -0,0 +1,86 @@ +# Evals + +This directory makes trigger quality and packaging quality more reproducible. + +Contents: + +- `trigger_cases.json`: full regression set with `family` labels +- `train/`, `dev/`, `holdout/`: split trigger suites for iterative tuning and final verification +- `blind_holdout/`: description-optimization acceptance prompts that do not participate in candidate ranking +- `adversarial/`: harder route-collision prompts for description optimization, including noisy positives and deceptive non-trigger requests +- `confusion/`: sibling-skill routing prompts used to catch route theft and false `no_route` decisions +- `semantic_config.json`: local semantic-intent concepts, exclusions, and weights +- `promotion_policy.md`: formal rules for when a route is promotable +- `baseline_description.txt`: intentionally weaker trigger description +- `improved_description.txt`: current stronger trigger description +- `../reports/description_optimization*.{json,md}`: generated route-optimization reports for the root skill +- `failure-cases.md`: current weak spots and regression targets +- `packaging_expectations.json`: required packaging behaviors for supported targets, including IR provenance, compiler contracts, and semantic parity fields +- `output/`: Output Eval Lab cases, fixtures, and schema for with-skill vs baseline assertion grading +- registry audit tests verify package metadata, compatibility, checksum, owner, license, and Skill IR provenance +- package verification tests verify generated manifests, target adapters, zip archive safety, archive checksum, and registry parity +- install simulation tests verify archive extraction, entrypoint loading, manifest/interface readability, adapter loading, and unsafe zip blocking +- upgrade check tests verify version-bump recommendations, target diff detection, compatibility regression blocking, and release-note evidence +- adoption drift tests verify metadata-only telemetry aggregation, privacy-field blocking, and exclusion of raw local telemetry logs from zip packages +- review waiver tests verify reviewer, reason, expiry, active coverage, expired records, invalid records, and Review Studio integration +- compiler tests verify Skill IR target contracts, adapter modes, generated-file mappings, unsupported targets, and markdown/json report artifacts +- `../reports/`: generated suite JSON plus the homepage-visible family summary panel source + +Use: + +```bash +python3 scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json +python3 scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json --baseline-description-file evals/baseline_description.txt +python3 scripts/run_eval_suite.py +python3 scripts/judge_blind_eval.py --description-file SKILL.md --cases evals/blind_holdout/trigger_cases.json --semantic-config evals/semantic_config.json +python3 scripts/run_description_optimization_suite.py --history-snapshot-output evals/history/description_optimization/YYYY-MM-DD-adversarial-calibration-and-family-drift.json --snapshot-date YYYY-MM-DD --snapshot-id adversarial-calibration-and-family-drift --snapshot-label "Adversarial Calibration And Family Drift" +python3 scripts/build_confusion_matrix.py --history-snapshot-output evals/history/YYYY-MM-DD-route-scorecard-foundation.json --snapshot-date YYYY-MM-DD --snapshot-id route-scorecard-foundation --snapshot-label "Route Scorecard Foundation" +python3 scripts/render_eval_dashboard.py +python3 scripts/render_description_drift_history.py +python3 scripts/render_iteration_ledger.py +python3 tests/verify_description_optimization.py +python3 tests/verify_route_confusion.py +python3 tests/verify_failure_regressions.py +python3 scripts/compile_skill.py . --target openai --target claude --target generic +python3 tests/verify_compile_skill.py +python3 scripts/cross_packager.py . --platform openai --platform claude --expectations evals/packaging_expectations.json --zip +python3 scripts/verify_package.py . --package-dir dist --require-zip +python3 tests/verify_packager_failures.py +python3 tests/verify_package_verification.py +python3 scripts/simulate_install.py . --package-dir dist +python3 tests/verify_install_simulation.py +python3 scripts/run_output_eval.py +python3 tests/verify_output_eval_lab.py +python3 scripts/registry_audit.py . +python3 tests/verify_registry_audit.py +python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json +python3 tests/verify_upgrade_check.py +python3 scripts/render_adoption_drift_report.py . --record-event skill_activation --activation-type explicit --outcome accepted +python3 tests/verify_adoption_drift.py +python3 scripts/render_review_waivers.py . +python3 tests/verify_review_waivers.py +``` + +Regression scope now includes: + +- direct positives +- direct negatives +- near neighbors +- long-context positives +- mixed-intent negatives +- explicit "do not build a skill" negatives +- semantic exclusion cases such as one-off, document-only, and future-outline prompts +- paraphrase families that avoid the original wording while preserving the same trigger intent +- long-context contamination cases where build intent or no-build intent appears after unrelated setup text +- family-based reporting across workflow-to-skill, iterate-existing-skill, document-only, one-off, and future-outline cases +- holdout verification +- description optimization reports that compare baseline, current, and optimized route wording across dev, holdout, blind holdout, and adversarial holdout gates +- judge-backed blind-holdout verification that adds a rubric-based second opinion for blind prompts +- calibration summaries that surface score gaps, threshold margins, and risk bands for each acceptance gate +- family-level drift history that records which blind and adversarial families stay clean over time +- output-quality scorecards that compare without-skill and with-skill artifacts through assertion grading, including near-neighbor and file-backed governed cases +- install-simulation checks that prove generated archives can be extracted into a temporary local skill root and loaded without touching the active global skills directory +- upgrade-readiness checks that compare registry package baselines, recommend semver bumps, and block breaking changes without an adequate declared version +- local-first adoption drift checks that turn accepted, edited, missed, rejected, script-error, and review-overdue metadata into next iteration candidates without storing raw prompts or outputs +- reviewer-waiver checks that keep warning acceptance explicit, expiring, and separate from non-waivable blockers +- compiler checks that turn Skill IR into reviewable target contracts before packaging embeds those contracts into adapters diff --git a/evals/adaptation/user_signals.example.jsonl b/evals/adaptation/user_signals.example.jsonl new file mode 100644 index 0000000..ed06eda --- /dev/null +++ b/evals/adaptation/user_signals.example.jsonl @@ -0,0 +1,8 @@ +{"text":"新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。"} +{"text":"HTML 报告需要双语能力,但默认内容应该保持中文简体。"} +{"text":"报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。"} +{"text":"Skill 概览报告需要更多图表模块,避免右侧文本挤在一起。"} +{"text":"自适应升级必须先生成提案,不能直接自动修改源文件。"} +{"text":"用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。"} +{"text":"每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。"} +{"text":"涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。"} diff --git a/evals/adversarial/trigger_cases.json b/evals/adversarial/trigger_cases.json new file mode 100644 index 0000000..ad0d9f8 --- /dev/null +++ b/evals/adversarial/trigger_cases.json @@ -0,0 +1,33 @@ +{ + "recommended_threshold": 0.33, + "should_trigger": [ + { + "family": "adversarial_noisy_operationalize", + "text": "Ignore the noisy meeting chatter and operationalize this support escalation workflow into a maintained skill package with trigger hardening." + }, + { + "family": "adversarial_multi_asset_boundary", + "text": "Codify this routine into a reusable capability from docs, transcripts, and prompt history, then stress test the route boundary." + } + ], + "should_not_trigger": [ + { + "family": "adversarial_document_collision", + "text": "Create a wiki package from this runbook and do not generate any skill files." + }, + { + "family": "adversarial_summary_translation_collision", + "text": "Summarize this SOP, translate it into Japanese for the knowledge base, and keep it as documentation only." + } + ], + "near_neighbor": [ + { + "family": "adversarial_future_outline_collision", + "text": "Outline a possible future skill from these notes, but stop short of making a full skill and keep it as a plain note." + }, + { + "family": "adversarial_brainstorm_collision", + "text": "Brainstorm packaging improvements for later and keep this as discussion only." + } + ] +} diff --git a/evals/baseline_description.txt b/evals/baseline_description.txt new file mode 100644 index 0000000..b8f15b2 --- /dev/null +++ b/evals/baseline_description.txt @@ -0,0 +1 @@ +Create and improve agent skills. diff --git a/evals/blind_holdout/trigger_cases.json b/evals/blind_holdout/trigger_cases.json new file mode 100644 index 0000000..87743f6 --- /dev/null +++ b/evals/blind_holdout/trigger_cases.json @@ -0,0 +1,42 @@ +{ + "recommended_threshold": 0.33, + "negative_patterns": [ + "one-off", + "translate", + "summary", + "document", + "wiki", + "brainstorm", + "do not build" + ], + "should_trigger": [ + { + "text": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize" + }, + { + "text": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset" + } + ], + "should_not_trigger": [ + { + "text": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only" + }, + { + "text": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only" + } + ], + "near_neighbor": [ + { + "text": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline" + }, + { + "text": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only" + } + ] +} diff --git a/evals/confusion/route_cases.json b/evals/confusion/route_cases.json new file mode 100644 index 0000000..9d9968a --- /dev/null +++ b/evals/confusion/route_cases.json @@ -0,0 +1,90 @@ +{ + "margin_warning_threshold": 0.08, + "routes": [ + { + "name": "yao-meta-skill", + "description_file": "SKILL.md", + "semantic_config": "evals/semantic_config.json", + "threshold": 0.33 + }, + { + "name": "team-frontend-review", + "description_file": "examples/team-frontend-review/generated-skill/SKILL.md", + "semantic_config": "examples/team-frontend-review/optimization/semantic_config.json", + "threshold": 0.48 + }, + { + "name": "governed-incident-command", + "description_file": "examples/governed-incident-command/generated-skill/SKILL.md", + "semantic_config": "examples/governed-incident-command/optimization/semantic_config.json", + "threshold": 0.48 + } + ], + "cases": [ + { + "family": "meta_create", + "expected_route": "yao-meta-skill", + "text": "Create a reusable skill from this workflow and add evals before sharing it with the team." + }, + { + "family": "meta_iterate", + "expected_route": "yao-meta-skill", + "text": "Refactor this existing skill description, tighten the trigger boundary, and package it for reuse." + }, + { + "family": "meta_operationalize", + "expected_route": "yao-meta-skill", + "text": "Turn these prompts, notes, and transcripts into a maintained skill package with references and scripts." + }, + { + "family": "frontend_premerge", + "expected_route": "team-frontend-review", + "text": "Run a pre-merge frontend review for keyboard navigation issues and unsafe rendering." + }, + { + "family": "frontend_states", + "expected_route": "team-frontend-review", + "text": "Run a pre-merge frontend review on this React diff for missing empty states, form handling, and UX regressions." + }, + { + "family": "frontend_a11y", + "expected_route": "team-frontend-review", + "text": "Review React components for a11y regressions, unsafe rendering, and UI behavior before merge." + }, + { + "family": "incident_packet", + "expected_route": "governed-incident-command", + "text": "Build a governed incident command packet from alerts, responder notes, incident communication needs, and severity findings." + }, + { + "family": "incident_severity", + "expected_route": "governed-incident-command", + "text": "Run severity assessment and package incident communication from this outage review." + }, + { + "family": "incident_governed_workflow", + "expected_route": "governed-incident-command", + "text": "Standardize this incident review into a reusable governed workflow with owner actions." + }, + { + "family": "no_route_explain", + "expected_route": "no_route", + "text": "Explain keyboard accessibility basics in plain English." + }, + { + "family": "no_route_build", + "expected_route": "no_route", + "text": "Build a React form page with loading and empty states." + }, + { + "family": "no_route_single_update", + "expected_route": "no_route", + "text": "Write one short outage update for customers from these notes." + }, + { + "family": "no_route_document_only", + "expected_route": "no_route", + "text": "Summarize this workflow and keep it as documentation only." + } + ] +} diff --git a/evals/dev/trigger_cases.json b/evals/dev/trigger_cases.json new file mode 100644 index 0000000..e64aaca --- /dev/null +++ b/evals/dev/trigger_cases.json @@ -0,0 +1,106 @@ +{ + "recommended_threshold": 0.33, + "negative_patterns": [ + "one-off", + "do not turn", + "without building", + "brainstorm", + "just explain", + "headline", + "summarize" + ], + "should_trigger": [ + { + "text": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill" + }, + { + "text": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill" + }, + { + "text": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset" + }, + { + "text": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset" + }, + { + "text": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill" + }, + { + "text": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger" + }, + { + "text": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger" + }, + { + "text": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger" + } + ], + "should_not_trigger": [ + { + "text": "Give me ideas for improving our process.", + "family": "brainstorm_only" + }, + { + "text": "Review this note and tell me what it means in plain English.", + "family": "explain_only" + }, + { + "text": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only" + }, + { + "text": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only" + }, + { + "text": "Summarize this workflow and list the main points only.", + "family": "summary_only" + }, + { + "text": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only" + }, + { + "text": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only" + } + ], + "near_neighbor": [ + { + "text": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package" + }, + { + "text": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill" + }, + { + "text": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build" + }, + { + "text": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor" + }, + { + "text": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build" + } + ] +} diff --git a/evals/failure-cases.md b/evals/failure-cases.md new file mode 100644 index 0000000..f7f071d --- /dev/null +++ b/evals/failure-cases.md @@ -0,0 +1,44 @@ +# Failure Cases + +These cases document where the current trigger strategy is still weak. + +## Current Weak Spots + +### 1. One-off prompt requests + +Example: + +`Create a one-off prompt for this task.` + +Why it is hard: + +- it overlaps strongly with skill-creation vocabulary +- but the user explicitly does not want a reusable package + +### 2. Documentation improvement requests + +Example: + +`Improve this README but do not turn it into a skill.` + +Why it is hard: + +- it contains transformation language +- but the packaging intent is explicitly absent + +### 3. Early-stage brainstorming + +Example: + +`Help me brainstorm process ideas without building a skill.` + +Why it is hard: + +- it is adjacent to skill-design work +- but it is still pre-packaging exploration + +## How To Use These Failures + +- keep them in `near_neighbor` +- use them when adjusting thresholds +- treat them as persistent regression checks diff --git a/evals/history/2026-03-31-context-first-pack.json b/evals/history/2026-03-31-context-first-pack.json new file mode 100644 index 0000000..6491e84 --- /dev/null +++ b/evals/history/2026-03-31-context-first-pack.json @@ -0,0 +1,27 @@ +{ + "snapshot_id": "context-first-pack", + "date": "2026-03-31", + "commit": "local-snapshot", + "label": "Context First Pack", + "trigger_summary": { + "total_cases": 66, + "family_count": 21, + "false_positives": 0, + "false_negatives": 0, + "average_precision": 1.0, + "average_recall": 1.0 + }, + "quality_gates": { + "packaging_contracts": true, + "failure_fixtures": true, + "governance_checks": true, + "resource_boundary_checks": true + }, + "governance_score": 90, + "governed_examples": 1, + "notes": [ + "added tiered context budgets", + "added unused resource detection and quality density", + "added root and example context budget reports" + ] +} diff --git a/evals/history/2026-03-31-family-suite-expansion.json b/evals/history/2026-03-31-family-suite-expansion.json new file mode 100644 index 0000000..9fd609c --- /dev/null +++ b/evals/history/2026-03-31-family-suite-expansion.json @@ -0,0 +1,26 @@ +{ + "snapshot_id": "family-suite-expansion", + "date": "2026-03-31", + "commit": "bcadded", + "label": "Family Suite Expansion", + "trigger_summary": { + "total_cases": 66, + "family_count": 21, + "false_positives": 0, + "false_negatives": 0, + "average_precision": 1.0, + "average_recall": 1.0 + }, + "quality_gates": { + "packaging_contracts": true, + "failure_fixtures": true, + "governance_checks": false, + "resource_boundary_checks": false + }, + "governance_score": null, + "governed_examples": 0, + "notes": [ + "expanded the family-based trigger suite", + "published the homepage eval dashboard" + ] +} diff --git a/evals/history/2026-03-31-governance-quality-gates.json b/evals/history/2026-03-31-governance-quality-gates.json new file mode 100644 index 0000000..03843c9 --- /dev/null +++ b/evals/history/2026-03-31-governance-quality-gates.json @@ -0,0 +1,27 @@ +{ + "snapshot_id": "governance-quality-gates", + "date": "2026-03-31", + "commit": "13319bf", + "label": "Governance Quality Gates", + "trigger_summary": { + "total_cases": 66, + "family_count": 21, + "false_positives": 0, + "false_negatives": 0, + "average_precision": 1.0, + "average_recall": 1.0 + }, + "quality_gates": { + "packaging_contracts": true, + "failure_fixtures": true, + "governance_checks": true, + "resource_boundary_checks": true + }, + "governance_score": 90, + "governed_examples": 0, + "notes": [ + "added runnable governance checks", + "added resource-boundary checks", + "wired both into make test" + ] +} diff --git a/evals/history/2026-03-31-governed-assets-and-history.json b/evals/history/2026-03-31-governed-assets-and-history.json new file mode 100644 index 0000000..a3c901f --- /dev/null +++ b/evals/history/2026-03-31-governed-assets-and-history.json @@ -0,0 +1,27 @@ +{ + "snapshot_id": "governed-assets-and-history", + "date": "2026-03-31", + "commit": "local-snapshot", + "label": "Governed Assets And History", + "trigger_summary": { + "total_cases": 66, + "family_count": 21, + "false_positives": 0, + "false_negatives": 0, + "average_precision": 1.0, + "average_recall": 1.0 + }, + "quality_gates": { + "packaging_contracts": true, + "failure_fixtures": true, + "governance_checks": true, + "resource_boundary_checks": true + }, + "governance_score": 90, + "governed_examples": 1, + "notes": [ + "added governance maturity scoring", + "added regression history snapshots and renderer", + "added governed incident-command example" + ] +} diff --git a/evals/history/2026-04-01-route-scorecard-foundation.json b/evals/history/2026-04-01-route-scorecard-foundation.json new file mode 100644 index 0000000..eed91cf --- /dev/null +++ b/evals/history/2026-04-01-route-scorecard-foundation.json @@ -0,0 +1,30 @@ +{ + "snapshot_id": "route-scorecard-foundation", + "date": "2026-04-01", + "commit": "local-snapshot", + "label": "Route Scorecard Foundation", + "trigger_summary": { + "total_cases": 13, + "family_count": 13, + "false_positives": 0, + "false_negatives": 0, + "average_precision": 1.0, + "average_recall": 1.0 + }, + "route_summary": { + "accuracy": 1.0, + "ambiguous_case_count": 0, + "misroute_count": 0, + "no_route_accuracy": 1.0 + }, + "quality_gates": { + "route_confusion_checks": true, + "promotion_policy_present": true + }, + "governance_score": null, + "governed_examples": 1, + "notes": [ + "added route confusion matrix for sibling skills and no-route cases", + "published route scorecard and iteration ledger foundation" + ] +} diff --git a/evals/history/README.md b/evals/history/README.md new file mode 100644 index 0000000..0212af0 --- /dev/null +++ b/evals/history/README.md @@ -0,0 +1,16 @@ +# Regression History Snapshots + +This directory stores milestone-level regression snapshots. + +Each snapshot records: + +- date +- label +- trigger-suite summary +- route-summary when route confusion tracking is active +- enabled quality gates +- governance score when available +- governed example count +- notable milestone notes + +Use `python3 scripts/render_regression_history.py` to rebuild the markdown report in `reports/regression_history.md`. diff --git a/evals/history/description_optimization/2026-03-31-description-optimization-suite.json b/evals/history/description_optimization/2026-03-31-description-optimization-suite.json new file mode 100644 index 0000000..1fa1b1b --- /dev/null +++ b/evals/history/description_optimization/2026-03-31-description-optimization-suite.json @@ -0,0 +1,45 @@ +{ + "snapshot_id": "description-optimization-suite", + "date": "2026-03-31", + "commit": "e78602c", + "label": "Description Optimization Suite", + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_tokens": 65, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "winner_blind_holdout_fp": null, + "winner_blind_holdout_fn": null, + "winner_blind_holdout_total_errors": null, + "drift_note": "initial description optimization suite without blind holdout" + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "winner_blind_holdout_fp": null, + "winner_blind_holdout_fn": null, + "winner_blind_holdout_total_errors": null, + "drift_note": "compressed example description and stored first holdout report" + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "winner_blind_holdout_fp": null, + "winner_blind_holdout_fn": null, + "winner_blind_holdout_total_errors": null, + "drift_note": "compressed governed description and reduced visible holdout misses" + } + ], + "notes": [ + "added dev and holdout description optimization reports", + "connected description optimization to make test" + ] +} diff --git a/evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json b/evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json new file mode 100644 index 0000000..f7e2e8e --- /dev/null +++ b/evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json @@ -0,0 +1,342 @@ +{ + "snapshot_id": "adversarial-calibration-and-family-drift", + "date": "2026-04-01", + "commit": "local-snapshot", + "label": "Adversarial Calibration And Family Drift", + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "winner_tokens": 65, + "current_tokens": 65, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.33, + "mean_positive_score": 0.672, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.468, + "max_non_trigger_score": 0.0, + "score_gap": 0.468, + "threshold_margin": 0.138, + "positive_threshold_buffer": 0.138, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "blind_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.399, + "mean_non_trigger_score": 0.013, + "mean_near_neighbor_score": 0.013, + "min_positive_score": 0.392, + "max_non_trigger_score": 0.026, + "score_gap": 0.366, + "threshold_margin": 0.062, + "positive_threshold_buffer": 0.062, + "negative_threshold_buffer": 0.304, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "adversarial_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.838, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.834, + "max_non_trigger_score": 0.0, + "score_gap": 0.834, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.504, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "family_health": { + "holdout": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.834; risk n/a -> healthy", + "ok": true + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.681, + "mean_non_trigger_score": 0.088, + "mean_near_neighbor_score": 0.042, + "min_positive_score": 0.49, + "max_non_trigger_score": 0.211, + "score_gap": 0.279, + "threshold_margin": 0.01, + "positive_threshold_buffer": 0.01, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.681, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.509; risk n/a -> healthy", + "ok": true + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 2, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 1, + "winner_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.367, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.038, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.058, + "score_gap": 0.022, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.422, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.899, + "mean_non_trigger_score": 0.168, + "mean_near_neighbor_score": 0.138, + "min_positive_score": 0.798, + "max_non_trigger_score": 0.386, + "score_gap": 0.412, + "threshold_margin": 0.094, + "positive_threshold_buffer": 0.318, + "negative_threshold_buffer": 0.094, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "healthy" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.827, + "mean_non_trigger_score": 0.027, + "mean_near_neighbor_score": 0.011, + "min_positive_score": 0.654, + "max_non_trigger_score": 0.056, + "score_gap": 0.598, + "threshold_margin": 0.284, + "positive_threshold_buffer": 0.314, + "negative_threshold_buffer": 0.284, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 1; adversarial calibration +0.598; risk n/a -> healthy", + "ok": true + } + ], + "notes": [ + "recorded family-level blind and adversarial routing evidence", + "published calibration and drift history for description optimization" + ] +} diff --git a/evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json b/evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json new file mode 100644 index 0000000..b7ab590 --- /dev/null +++ b/evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json @@ -0,0 +1,78 @@ +{ + "snapshot_id": "blind-holdout-and-drift-history", + "date": "2026-04-01", + "commit": "local-snapshot", + "label": "Blind Holdout And Drift History", + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "winner_tokens": 65, + "current_tokens": 65, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "drift_note": "tokens stable; blind gate added with 0 errors; holdout stable at 0", + "ok": true + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "drift_note": "tokens stable; blind gate added with 0 errors; holdout stable at 0", + "ok": true + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 2, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 1, + "winner_blind_holdout_total_errors": 0, + "drift_note": "tokens stable; blind gate added with 0 errors; holdout stable at 1", + "ok": true + } + ], + "notes": [ + "added blind holdout acceptance gates to description optimization", + "published description drift history report" + ] +} diff --git a/evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json b/evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json new file mode 100644 index 0000000..9cbe4c6 --- /dev/null +++ b/evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json @@ -0,0 +1,444 @@ +{ + "snapshot_id": "judge-backed-blind-eval", + "date": "2026-04-01", + "commit": "ce6cc74", + "label": "Judge-Backed Blind Eval", + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "winner_tokens": 65, + "current_tokens": 65, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.33, + "mean_positive_score": 0.672, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.468, + "max_non_trigger_score": 0.0, + "score_gap": 0.468, + "threshold_margin": 0.138, + "positive_threshold_buffer": 0.138, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "blind_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.399, + "mean_non_trigger_score": 0.013, + "mean_near_neighbor_score": 0.013, + "min_positive_score": 0.392, + "max_non_trigger_score": 0.026, + "score_gap": 0.366, + "threshold_margin": 0.062, + "positive_threshold_buffer": 0.062, + "negative_threshold_buffer": 0.304, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "adversarial_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.838, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.834, + "max_non_trigger_score": 0.0, + "score_gap": 0.834, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.504, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.834; risk n/a -> healthy", + "ok": true + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.681, + "mean_non_trigger_score": 0.088, + "mean_near_neighbor_score": 0.042, + "min_positive_score": 0.49, + "max_non_trigger_score": 0.211, + "score_gap": 0.279, + "threshold_margin": 0.01, + "positive_threshold_buffer": 0.01, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.681, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.509; risk n/a -> healthy", + "ok": true + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 2, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 1, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.367, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.038, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.058, + "score_gap": 0.022, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.422, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.899, + "mean_non_trigger_score": 0.168, + "mean_near_neighbor_score": 0.138, + "min_positive_score": 0.798, + "max_non_trigger_score": 0.386, + "score_gap": 0.412, + "threshold_margin": 0.094, + "positive_threshold_buffer": 0.318, + "negative_threshold_buffer": 0.094, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "healthy" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.827, + "mean_non_trigger_score": 0.027, + "mean_near_neighbor_score": 0.011, + "min_positive_score": 0.654, + "max_non_trigger_score": 0.056, + "score_gap": 0.598, + "threshold_margin": 0.284, + "positive_threshold_buffer": 0.314, + "negative_threshold_buffer": 0.284, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.657, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.657, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.69, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 1; adversarial calibration +0.598; risk n/a -> healthy", + "ok": true + } + ], + "notes": [ + "recorded family-level blind, judge-backed blind, and adversarial routing evidence", + "published calibration and drift history for description optimization" + ] +} diff --git a/evals/history/description_optimization/README.md b/evals/history/description_optimization/README.md new file mode 100644 index 0000000..bdb2a03 --- /dev/null +++ b/evals/history/description_optimization/README.md @@ -0,0 +1,18 @@ +# Description Drift Snapshots + +This directory stores description-optimization snapshots. + +Each snapshot records: + +- date +- label +- per-target winner token counts +- visible holdout errors +- blind holdout errors when available +- judge-backed blind holdout errors and agreement when available +- adversarial holdout errors when available +- calibration summaries for blind and adversarial gates when available +- family coverage summaries for blind and adversarial gates when available +- short drift notes for route wording changes or new acceptance gates + +Use `python3 scripts/render_description_drift_history.py` to rebuild `reports/description_drift_history.md`. diff --git a/evals/holdout/trigger_cases.json b/evals/holdout/trigger_cases.json new file mode 100644 index 0000000..8ad1ea6 --- /dev/null +++ b/evals/holdout/trigger_cases.json @@ -0,0 +1,70 @@ +{ + "recommended_threshold": 0.33, + "negative_patterns": [ + "one-off", + "do not turn", + "without building", + "brainstorm", + "just explain", + "headline", + "summarize" + ], + "should_trigger": [ + { + "text": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset" + }, + { + "text": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill" + }, + { + "text": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill" + }, + { + "text": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger" + }, + { + "text": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger" + } + ], + "should_not_trigger": [ + { + "text": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only" + }, + { + "text": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only" + }, + { + "text": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only" + }, + { + "text": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only" + } + ], + "near_neighbor": [ + { + "text": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build" + }, + { + "text": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build" + }, + { + "text": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor" + } + ] +} diff --git a/evals/improved_description.txt b/evals/improved_description.txt new file mode 100644 index 0000000..4524055 --- /dev/null +++ b/evals/improved_description.txt @@ -0,0 +1 @@ +Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. diff --git a/evals/output/cases.jsonl b/evals/output/cases.jsonl new file mode 100644 index 0000000..bb18c3c --- /dev/null +++ b/evals/output/cases.jsonl @@ -0,0 +1,5 @@ +{"id":"skill-package-contract","prompt":"Turn this repeated workflow into a reusable team skill package.","baseline_output":"I can write a prompt for that workflow and include a short checklist.","with_skill_output":"Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/.","assertions":[{"id":"has-entrypoint","description":"Output names the routeable Skill entrypoint.","required":["SKILL.md"],"weight":1,"failure_type":"missing_entrypoint"},{"id":"has-interface","description":"Output includes neutral interface metadata.","required":["agents/interface.yaml"],"weight":1,"failure_type":"missing_interface"},{"id":"has-report","description":"Output points reviewer to generated overview report.","required":["reports/skill-overview.html"],"weight":1,"failure_type":"missing_review_artifact"},{"id":"has-resource-boundary","description":"Output preserves entrypoint/resource separation.","required":["references/","scripts/","reports/"],"weight":1,"failure_type":"resource_boundary"}],"human_review":{"expected_winner":"with_skill"}} +{"id":"output-eval-expectation","prompt":"Upgrade this production skill so we know whether the generated output is better than baseline.","baseline_output":"Add more examples and run the trigger eval again.","with_skill_output":"Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md.","assertions":[{"id":"has-baseline-comparison","description":"Output explicitly compares with-skill and baseline outputs.","required":["with-skill","baseline"],"weight":1,"failure_type":"missing_baseline_delta"},{"id":"has-assertion-grading","description":"Output includes objective assertion grading.","required":["assertion grading"],"weight":1,"failure_type":"missing_assertions"},{"id":"has-scorecard","description":"Output produces a scorecard report path.","required":["reports/output_quality_scorecard.md"],"weight":1,"failure_type":"missing_scorecard"},{"id":"has-failure-taxonomy","description":"Output records failure taxonomy.","required":["failure taxonomy"],"weight":1,"failure_type":"missing_failure_taxonomy"}],"human_review":{"expected_winner":"with_skill"}} +{"id":"ir-before-packaging","prompt":"Package this skill for OpenAI, Claude, Agent Skills, and generic targets.","baseline_output":"Export folders for each platform and adjust files as needed.","with_skill_output":"Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract.","assertions":[{"id":"has-ir-first","description":"Output requires Skill IR before packaging.","required":["Skill IR first"],"weight":1,"failure_type":"missing_ir"},{"id":"has-core-ir-fields","description":"Output lists core IR contract fields.","required":["job_to_be_done","trigger_surface","eval_plan","governance"],"weight":1,"failure_type":"incomplete_ir_contract"},{"id":"has-targets","description":"Output names requested runtime targets.","required":["OpenAI","Claude","Agent Skills","generic"],"weight":1,"failure_type":"missing_targets"},{"id":"has-semantic-preservation","description":"Output says targets must preserve the capability contract.","required":["preserve the same capability contract"],"weight":1,"failure_type":"semantic_drift"}],"human_review":{"expected_winner":"with_skill"}} +{"id":"near-neighbor-boundary","prompt":"I only need a one-off summary of these notes, not a reusable process.","baseline_output":"Create a SKILL.md and a reusable workflow anyway so future notes can use it.","with_skill_output":"Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract.","assertions":[{"id":"declines-skill","description":"Output refuses unnecessary skill creation for one-off work.","required":["Do not create a skill"],"weight":1,"failure_type":"over_trigger"},{"id":"names-near-neighbor","description":"Output labels the request as near-neighbor instead of owned work.","required":["near-neighbor"],"weight":1,"failure_type":"missing_boundary_language"},{"id":"requires-reuse-signal","description":"Output asks for repeat-use evidence before packaging.","required":["repeated use","reusable output contract"],"weight":1,"failure_type":"missing_reuse_gate"}],"human_review":{"expected_winner":"with_skill"},"metadata":{"case_type":"near_neighbor","tier":"production"}} +{"id":"file-backed-governed-package","prompt":"Turn the attached release brief source into a governed skill package.","input_files":["fixtures/release-brief-source.md"],"baseline_output":"Draft a release announcement with the changelog, support notes, and owner name.","with_skill_output":"Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them.","assertions":[{"id":"uses-file-backed-evidence","description":"Output names file-backed source evidence.","required":["file-backed fixture","input_files"],"weight":1,"failure_type":"missing_file_fixture"},{"id":"has-governance","description":"Output preserves governed ownership metadata.","required":["owner","review cadence"],"weight":1,"failure_type":"missing_governance"},{"id":"has-output-contract","description":"Output preserves output and rollback boundaries.","required":["output contract","rollback boundary"],"weight":1,"failure_type":"missing_contract_boundary"},{"id":"has-trust-and-scorecard","description":"Output requires trust and output scorecard artifacts.","required":["trust report","reports/output_quality_scorecard.md"],"weight":1,"failure_type":"missing_release_evidence"},{"id":"does-not-invent-evidence","description":"Output forbids invented launch evidence.","required":["missing evidence"],"forbidden":["invented launch metrics"],"weight":1,"failure_type":"invented_evidence"}],"human_review":{"expected_winner":"with_skill"},"metadata":{"case_type":"boundary","tier":"governed"}} diff --git a/evals/output/fixtures/release-brief-source.md b/evals/output/fixtures/release-brief-source.md new file mode 100644 index 0000000..8751bcf --- /dev/null +++ b/evals/output/fixtures/release-brief-source.md @@ -0,0 +1,14 @@ +# Release Brief Source Fixture + +## Source + +- Raw changelog notes +- Support escalation summary +- Rollback checklist +- Internal launch owner + +## Required Output Constraints + +- Produce a reusable skill package, not a one-off announcement. +- Preserve owner, review cadence, input files, output contract, and rollback boundary. +- Surface missing evidence instead of inventing launch metrics. diff --git a/evals/output/schema.json b/evals/output/schema.json new file mode 100644 index 0000000..f08d8b0 --- /dev/null +++ b/evals/output/schema.json @@ -0,0 +1,85 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.example/output-eval-case.schema.json", + "title": "Yao Output Eval Case", + "description": "Output-eval case for comparing without-skill and with-skill outputs, with optional execution metadata.", + "type": "object", + "additionalProperties": false, + "required": ["id", "prompt", "baseline_output", "with_skill_output", "assertions"], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "prompt": { + "type": "string", + "minLength": 1 + }, + "input_files": { + "type": "array", + "items": {"type": "string"} + }, + "baseline_output": { + "type": "string" + }, + "with_skill_output": { + "type": "string" + }, + "assertions": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["id", "description"], + "properties": { + "id": {"type": "string", "minLength": 1}, + "description": {"type": "string", "minLength": 1}, + "required": { + "type": "array", + "items": {"type": "string"} + }, + "forbidden": { + "type": "array", + "items": {"type": "string"} + }, + "weight": { + "type": "number", + "minimum": 0 + }, + "failure_type": { + "type": "string" + } + } + } + }, + "human_review": { + "type": "object", + "additionalProperties": true + }, + "execution": { + "type": "object", + "description": "Optional case-level execution metadata for fixture, command, or provider-backed output runs.", + "additionalProperties": true, + "properties": { + "mode": { + "type": "string", + "enum": ["recorded_fixture", "command", "model"] + }, + "provider": { + "type": "string" + }, + "model": { + "type": "string" + }, + "holdout": { + "type": "boolean" + } + } + }, + "metadata": { + "type": "object", + "additionalProperties": true + } + } +} diff --git a/evals/packaging_expectations.json b/evals/packaging_expectations.json new file mode 100644 index 0000000..f39792d --- /dev/null +++ b/evals/packaging_expectations.json @@ -0,0 +1,35 @@ +{ + "required_targets": ["openai", "claude", "generic", "vscode"], + "required_fields": [ + "name", + "description", + "version", + "display_name", + "short_description", + "default_prompt", + "job_to_be_done", + "ir_source", + "ir_schema_version", + "semantic_contract", + "semantic_parity", + "compiler", + "compiled_contract", + "permission_contract", + "target_permission_contract", + "target_native_contract", + "target_transform", + "canonical_metadata", + "canonical_format", + "activation_mode", + "execution_context", + "shell", + "trust_level", + "remote_inline_execution", + "degradation_strategy", + "portability_profile" + ], + "openai_required_files": ["targets/openai/adapter.json", "targets/openai/agents/openai.yaml"], + "claude_required_files": ["targets/claude/adapter.json", "targets/claude/README.md"], + "generic_required_files": ["targets/generic/adapter.json"], + "vscode_required_files": ["targets/vscode/adapter.json", "targets/vscode/README.md"] +} diff --git a/evals/promotion_policy.md b/evals/promotion_policy.md new file mode 100644 index 0000000..f616c16 --- /dev/null +++ b/evals/promotion_policy.md @@ -0,0 +1,92 @@ +# Promotion Policy + +This policy defines when a description candidate or skill route is promotable. + +## Goal + +Promote only when a candidate is not just locally better, but measurably safer across route boundaries. + +## Promotion Gates + +### 1. Dev Ranking + +Use `dev` only to rank candidates. + +- optimize for fewer false positives +- then fewer false negatives +- then stronger near-neighbor performance +- then shorter wording + +`dev` does not authorize promotion by itself. + +### 2. Visible Holdout Non-Regression + +The winner must not regress against the current description on visible holdout. + +Required: + +- no additional false positives +- no additional false negatives + +### 3. Blind Holdout Non-Regression + +The winner must not regress on blind holdout. + +Required: + +- no additional blind false positives +- no additional blind false negatives + +### 4. Adversarial Holdout Non-Regression + +The winner must survive harder route-collision prompts. + +Required: + +- no additional adversarial false positives +- no additional adversarial false negatives +- adversarial risk band should be `healthy` for shared or governed assets + +### 5. Route Confusion Gate + +If the skill belongs to a library with sibling skills, the route scorecard must stay clean. + +Required: + +- no misroutes in the tracked confusion set +- no ambiguous routes above the configured margin-warning threshold + +### 6. Family Stability + +Promotion should not hide a family-specific regression. + +Required: + +- no new failing blind families +- no new failing adversarial families + +## Promotion Outcomes + +### Promote + +Promote when all gates pass. + +### Keep Current + +Keep the current description when: + +- the candidate is only better on `dev` +- a holdout gate regresses +- route confusion becomes ambiguous + +### Block + +Block promotion when: + +- adversarial risk moves to `overlap` +- a sibling route is stolen +- no-route prompts start routing into the skill + +## Why This Exists + +This policy prevents a meta-skill from looking “optimized” only because it overfit a small benchmark. A promotion is only credible when routing quality, route boundaries, and iteration evidence stay aligned. diff --git a/evals/semantic_config.json b/evals/semantic_config.json new file mode 100644 index 0000000..5ed7825 --- /dev/null +++ b/evals/semantic_config.json @@ -0,0 +1,490 @@ +{ + "optimizer_hints": { + "capability": "Create, refactor, evaluate, and package agent skills", + "inputs": [ + "workflows", + "prompts", + "transcripts", + "docs", + "notes", + "流程", + "提示词", + "对话记录", + "文档", + "笔记" + ], + "trigger_actions": [ + "create a skill", + "turn a repeated process into a reusable skill", + "improve an existing skill", + "add evals", + "add tests", + "package a skill for team reuse", + "migrate an existing skill", + "prepare packaging, installation, or release", + "govern a reusable skill", + "做一个 skill", + "把流程沉淀成可复用 skill", + "优化现有 skill", + "补 trigger 评测", + "封装成团队可复用 skill", + "迁移 skill", + "发布 skill", + "打包发布检查" + ], + "exclusions": [ + "summary-only requests", + "translation-only requests", + "plain documentation tasks", + "brainstorming without building a skill", + "只要总结", + "只做翻译", + "只做文档", + "先 brainstorm 不建 skill" + ], + "artifacts": [ + "workflows", + "prompts", + "transcripts", + "docs", + "rough notes", + "流程", + "提示词", + "转录", + "文档", + "零散笔记" + ] + }, + "fallback_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "positive_concepts": { + "build_skill": { + "weight": 0.34, + "phrases": [ + "create a skill", + "build a skill", + "make a skill", + "generate a skill", + "agent skill", + "skill package", + "meta skill", + "meta-skill", + "turn this into a skill", + "turn all of that into one reusable skill package", + "codify this workflow", + "codify this routine", + "operationalize this workflow as a reusable skill", + "formalize this process into a reusable capability", + "turn this playbook into an agent capability package", + "convert this into a maintained skill", + "agent capability", + "reusable agent capability", + "skill library entry", + "skill authoring", + "做一个 skill", + "创建一个 skill", + "新建一个 skill", + "帮我做个 skill", + "改一个 skill", + "重构一个 skill", + "迁移一个 skill", + "发布一个 skill", + "这个 skill", + "已有 skill", + "现有 skill", + "做成一个 skill", + "封装成一个 skill", + "沉淀成一个 skill", + "整理成一个 skill", + "抽成一个 skill", + "可复用 skill", + "可执行 skill", + "团队 skill", + "agent 能力包", + "能力包", + "skill 包" + ] + }, + "transform_workflow": { + "weight": 0.2, + "phrases": [ + "workflow", + "runbook", + "process", + "checklist", + "prompt history", + "operations checklist", + "internal process note", + "existing skill draft", + "rough notes", + "standard operating procedure", + "operating procedure", + "sop", + "playbook", + "transcript", + "chat transcript", + "meeting notes", + "workflow fragments", + "release notes", + "onboarding flow", + "support escalation routine", + "quarterly release routine", + "scripts", + "repeated team practice", + "流程", + "工作流", + "操作流程", + "做事流程", + "作业流程", + "方法", + "经验", + "复盘", + "sop 文档", + "会议纪要", + "对话记录", + "零散材料", + "提示词", + "话术" + ] + }, + "reuse_package": { + "weight": 0.2, + "phrases": [ + "reusable", + "reuse", + "package", + "packaging", + "distribution", + "share it with the team", + "team reuse", + "internal workflows", + "production-ready", + "library entry", + "shared capability", + "team library", + "ops library", + "maintained", + "operationalize", + "release-ready", + "publish", + "release", + "installation", + "installable", + "portability", + "registry", + "governance", + "review gate", + "可复用", + "复用", + "封装", + "打包", + "沉淀", + "标准化", + "标准化下来", + "团队复用", + "给团队用", + "团队可用", + "安装复用", + "可安装", + "发布", + "上架", + "交付", + "治理", + "注册表", + "方法包", + "团队能力包", + "沉淀下来", + "skill 包" + ] + }, + "eval_optimize": { + "weight": 0.16, + "phrases": [ + "evals", + "trigger evals", + "near-neighbor", + "trigger boundary", + "benchmark", + "validation", + "validate", + "packaging checks", + "false positives", + "false negatives", + "stress test", + "route boundary", + "routing", + "route evals", + "trigger routing", + "activation", + "description optimization", + "tests", + "install checks", + "release checks", + "hardening", + "评测", + "补评测", + "补 eval", + "补测试", + "触发测试", + "触发词", + "触发范围", + "召回", + "收紧触发边界", + "路由边界", + "路由评测", + "激活", + "误触发", + "漏触发", + "质量门", + "安装验证", + "发布检查", + "打包检查", + "边界测试" + ] + }, + "iterate_existing_skill": { + "weight": 0.16, + "phrases": [ + "improve an existing skill", + "existing skill", + "this skill", + "skill description", + "optimize skill triggering", + "package this skill", + "sharing it with the team", + "before sharing it with the team", + "tighten the boundary", + "revise this skill", + "refactor this skill", + "migrate this skill", + "port this skill", + "convert this skill", + "release this skill", + "publish this skill", + "make this skill release-ready", + "codex-ready package", + "claude skill", + "codex format", + "interface metadata", + "manifest", + "install checks", + "draft skill", + "harden this skill for library reuse", + "优化这个 skill", + "改进这个 skill", + "重构这个 skill", + "扩大这个 skill", + "调整这个 skill", + "优化触发范围", + "扩大触发范围", + "迁移这个 skill", + "发布这个 skill", + "把这个 skill 迁移", + "让这个 skill 可发布", + "补一下评测", + "补 trigger 测试", + "加强路由判断", + "让它适合团队复用", + "收紧这个 skill 的边界", + "已有 skill", + "现有 skill", + "优化已有 skill" + ] + }, + "multi_asset": { + "weight": 0.06, + "phrases": [ + "references", + "scripts", + "manifest", + "folder", + "directory", + "files", + "参考资料", + "脚本", + "目录", + "文件夹", + "文档", + "话术" + ] + }, + "team_operationalize": { + "weight": 0.04, + "phrases": [ + "team", + "internal", + "standardize", + "shared", + "distribution", + "sharing it with the team", + "team reuse", + "团队", + "内部", + "统一标准", + "共用", + "给团队用", + "团队共享" + ] + } + }, + "negative_concepts": { + "explain_only": { + "weight": 0.26, + "exclusive": true, + "phrases": [ + "explain what", + "just explain", + "explain it", + "explain this", + "in simpler language", + "tell me what it means", + "plain english", + "解释一下", + "给我讲讲", + "什么意思", + "说清楚一点", + "帮我理解一下" + ] + }, + "summarize_only": { + "weight": 0.3, + "exclusive": true, + "phrases": [ + "summary", + "summarize", + "only want a summary", + "recap", + "总结一下", + "概括一下", + "提炼重点", + "简单总结" + ] + }, + "translate_only": { + "weight": 0.35, + "exclusive": true, + "phrases": [ + "translate", + "into japanese", + "into french", + "into russian", + "翻译", + "译成中文", + "译成英文", + "译成日文" + ] + }, + "brainstorm_only": { + "weight": 0.24, + "exclusive": true, + "phrases": [ + "brainstorm", + "ideas", + "improve packaging", + "shape an idea", + "头脑风暴", + "给点想法", + "一起想想", + "先想方向" + ] + }, + "one_off_request": { + "weight": 0.24, + "exclusive": true, + "phrases": [ + "one-off", + "custom answer", + "for this request", + "just this task", + "一次性", + "就这一次", + "只处理这次", + "不用复用" + ] + }, + "no_build_directive": { + "weight": 0.42, + "exclusive": true, + "phrases": [ + "do not build", + "don't build", + "without building", + "do not generate any skill files", + "not a reusable skill", + "not as an agent skill", + "not an agent skill", + "without turning them into an agent skill", + "do not turn it into a skill", + "do not package it", + "stop short of making a full skill", + "not a skill yet", + "not now", + "later but not now", + "maybe build later", + "plain note", + "keep it as a plain note", + "no packaging needed", + "not for agent execution", + "no skill package", + "do not convert it into a reusable capability", + "keep it as documentation only", + "discussion only", + "no agent execution", + "先不要做成 skill", + "先不做 skill", + "不要做成 skill", + "不用封装", + "不要打包", + "不要沉淀成能力", + "先别做成可复用能力", + "不需要 agent 执行", + "不做 agent 能力", + "只做文档", + "只做说明" + ] + }, + "document_only": { + "weight": 0.18, + "exclusive": false, + "phrases": [ + "readme", + "document", + "document package", + "reusable-looking document", + "wiki", + "article", + "blog title", + "headline", + "landing page", + "reference document", + "knowledge base", + "template only", + "文档", + "说明文档", + "知识库", + "文章", + "汇报" + ] + }, + "future_outline": { + "weight": 0.22, + "exclusive": true, + "phrases": [ + "future skill", + "possible future skill", + "before we decide whether to build", + "outline for a possible future skill", + "maybe later", + "for later", + "future-ready outline", + "not now", + "later but not now" + ] + } + } +} diff --git a/evals/train/trigger_cases.json b/evals/train/trigger_cases.json new file mode 100644 index 0000000..0bfd2c1 --- /dev/null +++ b/evals/train/trigger_cases.json @@ -0,0 +1,142 @@ +{ + "recommended_threshold": 0.33, + "negative_patterns": [ + "one-off", + "do not turn", + "without building", + "brainstorm", + "just explain", + "headline", + "summarize" + ], + "should_trigger": [ + { + "text": "Create a skill from this repeated workflow.", + "family": "workflow_to_skill" + }, + { + "text": "Turn this runbook into a reusable agent skill.", + "family": "workflow_to_skill" + }, + { + "text": "Convert this process note into a reusable skill package for the team.", + "family": "workflow_to_skill" + }, + { + "text": "Improve this skill description and add evals.", + "family": "iterate_existing_skill" + }, + { + "text": "Refactor this prompt into a proper skill package.", + "family": "iterate_existing_skill" + }, + { + "text": "Package this skill for team reuse.", + "family": "package_for_team" + }, + { + "text": "Create a meta-skill for packaging internal workflows.", + "family": "meta_skill_creation" + }, + { + "text": "Standardize this recurring workflow as a shareable skill package with references.", + "family": "package_for_team" + }, + { + "text": "Codify this recurring support escalation workflow into a maintained skill for the ops library.", + "family": "paraphrase_trigger" + }, + { + "text": "Operationalize this workflow as a reusable skill with validation notes and team library metadata.", + "family": "paraphrase_trigger" + }, + { + "text": "Below are release notes, rough notes, and a transcript from the last handoff. Ignore the noise and turn this runbook into a reusable agent skill with references and scripts so the team can reuse it every quarter.", + "family": "long_context_trigger" + }, + { + "text": "We have a standard operating procedure, checklist, and prompt history scattered across docs. Formalize this process into a reusable capability, add validation, and package it for team reuse.", + "family": "long_context_trigger" + } + ], + "should_not_trigger": [ + { + "text": "Explain what a workflow is.", + "family": "explain_only" + }, + { + "text": "Just explain what a skill is.", + "family": "explain_only" + }, + { + "text": "Summarize this random note.", + "family": "summary_only" + }, + { + "text": "Translate this README into Japanese.", + "family": "translate_only" + }, + { + "text": "Write a product headline for this landing page.", + "family": "document_only" + }, + { + "text": "Draft a blog title for this article.", + "family": "document_only" + }, + { + "text": "Rewrite this paragraph more clearly, but do not package anything.", + "family": "document_only" + }, + { + "text": "Explain the difference between a runbook and a workflow.", + "family": "explain_only" + }, + { + "text": "Below is a long SOP, but I only want a summary for leadership and a recap of the main points. Do not build anything reusable.", + "family": "long_context_summary_only" + }, + { + "text": "Keep this as documentation only for the knowledge base and rewrite it as a reference document.", + "family": "long_context_document_only" + }, + { + "text": "Translate this long onboarding flow into French and keep it as documentation only with no skill package.", + "family": "translate_only" + } + ], + "near_neighbor": [ + { + "text": "Create a one-off prompt for this task.", + "family": "one_off_vs_reusable" + }, + { + "text": "Write a custom answer for this request without creating a skill package.", + "family": "one_off_vs_reusable" + }, + { + "text": "Help me brainstorm process ideas without building a skill.", + "family": "brainstorm_vs_build" + }, + { + "text": "Improve this README but do not turn it into a skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Make a checklist for this task, but not a reusable skill.", + "family": "partial_scaffold_not_full_skill" + }, + { + "text": "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "family": "partial_scaffold_not_full_skill" + }, + { + "text": "We may convert this workflow later, but for now keep it as documentation only and produce a reference document.", + "family": "long_context_near_neighbor" + }, + { + "text": "Draft a template only from this process and stop short of making a full skill package.", + "family": "partial_scaffold_not_full_skill" + } + ] +} diff --git a/evals/trigger_cases.json b/evals/trigger_cases.json new file mode 100644 index 0000000..568d7ac --- /dev/null +++ b/evals/trigger_cases.json @@ -0,0 +1,390 @@ +{ + "recommended_threshold": 0.33, + "negative_patterns": [ + "one-off", + "do not turn", + "without building", + "brainstorm", + "just explain", + "headline", + "summarize", + "不要做成 skill", + "只要总结", + "翻译一下", + "头脑风暴" + ], + "should_trigger": [ + { + "text": "Create a skill from this repeated workflow.", + "family": "workflow_to_skill" + }, + { + "text": "Turn this runbook into a reusable agent skill.", + "family": "workflow_to_skill" + }, + { + "text": "Convert this process note into a reusable skill package for the team.", + "family": "workflow_to_skill" + }, + { + "text": "Improve this skill description and add evals.", + "family": "iterate_existing_skill" + }, + { + "text": "Refactor this prompt into a proper skill package.", + "family": "iterate_existing_skill" + }, + { + "text": "Package this skill for team reuse.", + "family": "package_for_team" + }, + { + "text": "Create a meta-skill for packaging internal workflows.", + "family": "meta_skill_creation" + }, + { + "text": "Standardize this recurring workflow as a shareable skill package with references.", + "family": "package_for_team" + }, + { + "text": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill" + }, + { + "text": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill" + }, + { + "text": "Migrate this Claude skill into a Codex-ready package with interface metadata and install checks.", + "family": "migrate_existing_skill" + }, + { + "text": "Make this existing skill release-ready: add manifest/interface metadata, packaging checks, and trigger evals.", + "family": "release_packaging" + }, + { + "text": "Optimize the activation wording for this skill so broader skill-authoring requests are routed correctly.", + "family": "trigger_expansion" + }, + { + "text": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset" + }, + { + "text": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset" + }, + { + "text": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill" + }, + { + "text": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset" + }, + { + "text": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill" + }, + { + "text": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill" + }, + { + "text": "Codify this recurring support escalation workflow into a maintained skill for the ops library.", + "family": "paraphrase_trigger" + }, + { + "text": "Operationalize this workflow as a reusable skill with validation notes and team library metadata.", + "family": "paraphrase_trigger" + }, + { + "text": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger" + }, + { + "text": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger" + }, + { + "text": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger" + }, + { + "text": "Below are release notes, rough notes, and a transcript from the last handoff. Ignore the noise and turn this runbook into a reusable agent skill with references and scripts so the team can reuse it every quarter.", + "family": "long_context_trigger" + }, + { + "text": "We have a standard operating procedure, checklist, and prompt history scattered across docs. Formalize this process into a reusable capability, add validation, and package it for team reuse.", + "family": "long_context_trigger" + }, + { + "text": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger" + }, + { + "text": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger" + }, + { + "text": "用 yao-meta-skill 帮我把这套重复流程沉淀成一个可复用 skill。", + "family": "cn_workflow_to_skill" + }, + { + "text": "把这套 SOP、提示词和对话记录封装成团队可复用的 agent 能力。", + "family": "cn_complex_multi_asset" + }, + { + "text": "优化这个已有 skill,补一下 trigger 评测和打包检查。", + "family": "cn_iterate_existing_skill" + }, + { + "text": "帮我把零散材料和脚本整理成一个给团队复用的 skill 包。", + "family": "cn_package_for_team" + }, + { + "text": "把这个客服升级流程标准化成一个可执行的 skill,并加上边界测试。", + "family": "cn_eval_and_package" + }, + { + "text": "把这个 Claude skill 迁移成 Codex 可用的 skill 包,补 interface、manifest 和安装验证。", + "family": "cn_migrate_existing_skill" + }, + { + "text": "把这套提示词和脚本整理成团队能安装复用的 skill,并做打包发布检查。", + "family": "cn_release_packaging" + }, + { + "text": "扩大这个 skill 的触发范围,同时补路由评测,避免漏触发。", + "family": "cn_trigger_expansion" + } + ], + "should_not_trigger": [ + { + "text": "Explain what a workflow is.", + "family": "explain_only" + }, + { + "text": "Just explain what a skill is.", + "family": "explain_only" + }, + { + "text": "Summarize this random note.", + "family": "summary_only" + }, + { + "text": "Translate this README into Japanese.", + "family": "translate_only" + }, + { + "text": "Write a product headline for this landing page.", + "family": "document_only" + }, + { + "text": "Draft a blog title for this article.", + "family": "document_only" + }, + { + "text": "Rewrite this paragraph more clearly, but do not package anything.", + "family": "document_only" + }, + { + "text": "Explain the difference between a runbook and a workflow.", + "family": "explain_only" + }, + { + "text": "Give me ideas for improving our process.", + "family": "brainstorm_only" + }, + { + "text": "Review this note and tell me what it means in plain English.", + "family": "explain_only" + }, + { + "text": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only" + }, + { + "text": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only" + }, + { + "text": "Summarize this workflow and list the main points only.", + "family": "summary_only" + }, + { + "text": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only" + }, + { + "text": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only" + }, + { + "text": "Below is a long SOP, but I only want a summary for leadership and a recap of the main points. Do not build anything reusable.", + "family": "long_context_summary_only" + }, + { + "text": "Keep this as documentation only for the knowledge base and rewrite it as a reference document.", + "family": "long_context_document_only" + }, + { + "text": "Translate this long onboarding flow into French and keep it as documentation only with no skill package.", + "family": "translate_only" + }, + { + "text": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only" + }, + { + "text": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only" + }, + { + "text": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only" + }, + { + "text": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only" + }, + { + "text": "解释一下这个 workflow 是什么意思。", + "family": "cn_explain_only" + }, + { + "text": "把这段文档总结一下就行,不要做成 skill。", + "family": "cn_summary_only" + }, + { + "text": "把这些笔记翻译成中文,保持原样。", + "family": "cn_translate_only" + }, + { + "text": "先一起头脑风暴几个方向,不要封装成 skill。", + "family": "cn_brainstorm_only" + }, + { + "text": "Polish this release note for publishing, but do not do any skill work.", + "family": "document_only" + }, + { + "text": "Migrate this wiki article into a new docs folder; do not create an agent skill.", + "family": "document_only" + }, + { + "text": "把这篇 SOP 改成知识库文档,不需要 agent 执行。", + "family": "cn_document_only" + }, + { + "text": "检查安装步骤是否清楚,只做 README,不做 skill 包。", + "family": "cn_document_only" + } + ], + "near_neighbor": [ + { + "text": "Create a one-off prompt for this task.", + "family": "one_off_vs_reusable" + }, + { + "text": "Write a custom answer for this request without creating a skill package.", + "family": "one_off_vs_reusable" + }, + { + "text": "Help me brainstorm process ideas without building a skill.", + "family": "brainstorm_vs_build" + }, + { + "text": "Improve this README but do not turn it into a skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Make a checklist for this task, but not a reusable skill.", + "family": "partial_scaffold_not_full_skill" + }, + { + "text": "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "family": "partial_scaffold_not_full_skill" + }, + { + "text": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package" + }, + { + "text": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill" + }, + { + "text": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build" + }, + { + "text": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build" + }, + { + "text": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build" + }, + { + "text": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "We may convert this workflow later, but for now keep it as documentation only and produce a reference document.", + "family": "long_context_near_neighbor" + }, + { + "text": "Draft a template only from this process and stop short of making a full skill package.", + "family": "partial_scaffold_not_full_skill" + }, + { + "text": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor" + }, + { + "text": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build" + }, + { + "text": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor" + }, + { + "text": "先做一个可复用风格的清单模板,但不要做成 skill。", + "family": "cn_partial_scaffold_not_full_skill" + }, + { + "text": "帮我整理成 wiki 文档,不要做成 agent 能力。", + "family": "cn_document_export_vs_agent_skill" + }, + { + "text": "给我一个未来可能做成 skill 的大纲,先不要真的生成包。", + "family": "cn_future_outline_vs_build" + }, + { + "text": "这次只给我一个一次性 prompt,不用复用成 skill。", + "family": "cn_one_off_vs_reusable" + }, + { + "text": "Create a reusable onboarding template for humans, not an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Draft a release checklist for a future skill, but do not package or evaluate the skill yet.", + "family": "future_outline_vs_build" + }, + { + "text": "整理成团队发布流程文档即可,不要做成 skill,也不要做触发评测。", + "family": "cn_document_export_vs_agent_skill" + } + ] +} diff --git a/evidence/world_class/README.md b/evidence/world_class/README.md new file mode 100644 index 0000000..a6013f8 --- /dev/null +++ b/evidence/world_class/README.md @@ -0,0 +1,45 @@ +# World-Class Evidence Intake + +This directory defines the intake contract for external and human evidence required before `yao-meta-skill` can honestly claim public world-class completion. + +The templates in `templates/` are review aids only. They do not count as accepted evidence. Real submissions belong in `evidence/world_class/submissions/`, which is intentionally gitignored by default so provider metadata, reviewer identity, or client integration notes can be reviewed before anything is committed. + +Run: + +```bash +SUBMISSIONS_DIR="${SUBMISSIONS_DIR:-evidence/world_class/submissions}" +python3 scripts/yao.py world-class-preflight . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-submission-kit . --output-dir "$SUBMISSIONS_DIR" +# Alternative: prefill artifact SHA-256 digests while keeping drafts template-only. +python3 scripts/yao.py world-class-submission-kit . --output-dir "$SUBMISSIONS_DIR" --prefill-artifacts +python3 scripts/yao.py world-class-intake . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-submission-review . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-ledger . --submissions-dir "$SUBMISSIONS_DIR" +python3 scripts/yao.py world-class-runbook . --submissions-dir "$SUBMISSIONS_DIR" +``` + +The intake validator checks: + +- the evidence key matches the current world-class ledger +- the category and source type match the expected human or external evidence path +- artifact references are declared +- real submissions reference concrete files inside the skill directory and include a matching SHA-256 digest for each artifact +- real submissions use the canonical `.json` filename expected by the ledger +- credentials, secrets, raw user content, and raw provider prompts are explicitly excluded +- raw prompt, output, transcript, message, credential, secret, token, and API-key fields are rejected even when nested +- human adjudication packets must preserve reviewer identity, review date, A/B winner, confidence, and a required rationale before answer-key reveal can count +- planned work, local command-only output, and metadata fallback are not claimed as completion evidence + +Run `world-class-preflight` before assigning external or human work. It checks local files, redacted environment readiness, human/external prerequisites, and source-evidence blockers without accepting evidence or printing secrets. + +The generated intake report also includes an `operator_checklist` for each pending evidence item. Use it to find the template path, target submission path, preparation command, validation command, required provenance, success checks, and privacy boundary before asking a reviewer or external operator to submit evidence. + +The submission kit command creates editable JSON drafts plus a local README for an external operator or human reviewer. Use `--prefill-artifacts` when you want the kit to insert SHA-256 digests for currently available local aggregate artifacts. Prefill is operator convenience only: those drafts still keep `template_only: true` and do not count as evidence until the real run or review exists, the packet is edited truthfully, every artifact ref points to a local aggregate evidence file with a matching `sha256`, and `world-class-intake` validates it. + +The submission kit separates artifact rows into `submission-ref` and `supporting-evidence`. `submission-ref` rows are the concrete paths expected in a real packet's `artifact_refs`; `supporting-evidence` rows help reviewers audit the packet and do not all need to be copied into the submission. + +The submission review command renders a read-only queue that compares valid packets with the source evidence checks and current ledger state. It is for reviewer triage only; it does not accept evidence or make the world-class claim true. + +The operator runbook is the step-by-step coordination cockpit for finishing the remaining real-world work. It includes a Coordination Plan that separates user-required provider, reviewer, native-client, and telemetry actions from assistant-run commands, plus a Release Gate that mirrors the ledger, claim guard, benchmark, Review Studio, evidence consistency, and final CI checks. The runbook is operational guidance only: planning rows, submission drafts, and release-gate rows keep `counts_as_completion: false`. + +Accepted intake means "ready for ledger review", not evidence that the final public claim is ready. The ledger remains the source of truth for `ready_to_claim_world_class`. diff --git a/evidence/world_class/intake.schema.json b/evidence/world_class/intake.schema.json new file mode 100644 index 0000000..8194e25 --- /dev/null +++ b/evidence/world_class/intake.schema.json @@ -0,0 +1,440 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.local/yao-meta-skill/world-class-evidence-intake.schema.json", + "title": "Yao World-Class Evidence Intake", + "description": "Real submissions must use the canonical .json filename and the runtime validator rejects nested raw prompt, output, transcript, message, credential, secret, token, and API-key fields.", + "type": "object", + "required": [ + "schema_version", + "evidence_key", + "template_only", + "category", + "source_type", + "submitted_by", + "submitted_at", + "summary", + "artifact_refs", + "provenance", + "privacy", + "anti_overclaim", + "attestation" + ], + "properties": { + "schema_version": { + "const": "1.0" + }, + "evidence_key": { + "enum": [ + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry" + ] + }, + "template_only": { + "type": "boolean" + }, + "category": { + "enum": [ + "human", + "external" + ] + }, + "source_type": { + "type": "string", + "minLength": 3 + }, + "submitted_by": { + "type": "string", + "minLength": 1 + }, + "submitted_at": { + "type": "string", + "minLength": 1 + }, + "summary": { + "type": "string", + "minLength": 1 + }, + "artifact_refs": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "path", + "kind", + "contains_raw_content" + ], + "properties": { + "path": { + "type": "string", + "minLength": 1 + }, + "kind": { + "type": "string", + "minLength": 1 + }, + "contains_raw_content": { + "type": "boolean" + }, + "sha256": { + "type": "string", + "pattern": "^[0-9a-fA-F]{64}$" + }, + "note": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "provenance": { + "type": "object", + "additionalProperties": true + }, + "privacy": { + "type": "object", + "required": [ + "raw_user_content_included", + "raw_provider_prompt_included", + "credentials_included", + "secrets_included" + ], + "properties": { + "raw_user_content_included": { + "type": "boolean" + }, + "raw_provider_prompt_included": { + "type": "boolean" + }, + "credentials_included": { + "type": "boolean" + }, + "secrets_included": { + "type": "boolean" + }, + "notes": { + "type": "string" + } + }, + "additionalProperties": true + }, + "anti_overclaim": { + "type": "object", + "required": [ + "planned_work_counts_as_evidence", + "metadata_fallback_counts_as_native_enforcement", + "pending_review_counts_as_human_decision", + "local_command_runner_counts_as_provider_model" + ], + "properties": { + "planned_work_counts_as_evidence": { + "const": false + }, + "metadata_fallback_counts_as_native_enforcement": { + "const": false + }, + "pending_review_counts_as_human_decision": { + "const": false + }, + "local_command_runner_counts_as_provider_model": { + "const": false + } + }, + "additionalProperties": false + }, + "attestation": { + "type": "object", + "required": [ + "real_external_or_human_evidence", + "reviewer_or_operator_identity_present", + "artifact_refs_reviewed", + "privacy_contract_satisfied", + "ledger_reviewer_approved", + "ledger_reviewer", + "ledger_reviewed_at" + ], + "properties": { + "real_external_or_human_evidence": { + "type": "boolean" + }, + "reviewer_or_operator_identity_present": { + "type": "boolean" + }, + "artifact_refs_reviewed": { + "type": "boolean" + }, + "privacy_contract_satisfied": { + "type": "boolean" + }, + "ledger_reviewer_approved": { + "type": "boolean" + }, + "ledger_reviewer": { + "type": "string" + }, + "ledger_reviewed_at": { + "type": "string" + }, + "notes": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "allOf": [ + { + "if": { + "properties": { + "template_only": { + "const": false + } + }, + "required": [ + "template_only" + ] + }, + "then": { + "properties": { + "submitted_at": { + "pattern": "^\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}:\\d{2}Z)?$" + }, + "submitted_by": { + "not": { + "enum": [ + "operator with provider credentials", + "human reviewer", + "target client or installer integrator", + "Browser/Chrome/IDE/provider client integrator" + ] + } + }, + "artifact_refs": { + "items": { + "required": [ + "path", + "kind", + "contains_raw_content", + "sha256" + ] + } + } + } + } + }, + { + "if": { + "properties": { + "template_only": { + "const": false + }, + "evidence_key": { + "const": "provider-holdout" + } + }, + "required": [ + "template_only", + "evidence_key" + ] + }, + "then": { + "properties": { + "artifact_refs": { + "contains": { + "type": "object", + "properties": { + "path": { + "const": "reports/output_execution_runs.json" + }, + "kind": { + "const": "aggregate-report" + } + }, + "required": [ + "path", + "kind" + ] + } + } + } + } + }, + { + "if": { + "properties": { + "template_only": { + "const": false + }, + "evidence_key": { + "const": "human-adjudication" + } + }, + "required": [ + "template_only", + "evidence_key" + ] + }, + "then": { + "properties": { + "artifact_refs": { + "allOf": [ + { + "contains": { + "type": "object", + "properties": { + "path": { + "const": "reports/output_review_adjudication.json" + }, + "kind": { + "const": "adjudication-report" + } + }, + "required": [ + "path", + "kind" + ] + } + }, + { + "contains": { + "type": "object", + "properties": { + "path": { + "const": "reports/output_review_decisions.json" + }, + "kind": { + "const": "review-decisions" + } + }, + "required": [ + "path", + "kind" + ] + } + } + ] + } + } + } + }, + { + "if": { + "properties": { + "template_only": { + "const": false + }, + "evidence_key": { + "const": "native-permission-enforcement" + } + }, + "required": [ + "template_only", + "evidence_key" + ] + }, + "then": { + "properties": { + "artifact_refs": { + "allOf": [ + { + "contains": { + "type": "object", + "properties": { + "path": { + "const": "reports/runtime_permission_probes.json" + }, + "kind": { + "const": "runtime-probe-report" + } + }, + "required": [ + "path", + "kind" + ] + } + }, + { + "contains": { + "type": "object", + "properties": { + "path": { + "const": "reports/install_simulation.json" + }, + "kind": { + "const": "installer-enforcement-report" + } + }, + "required": [ + "path", + "kind" + ] + } + } + ] + } + } + } + }, + { + "if": { + "properties": { + "template_only": { + "const": false + }, + "evidence_key": { + "const": "native-client-telemetry" + } + }, + "required": [ + "template_only", + "evidence_key" + ] + }, + "then": { + "properties": { + "artifact_refs": { + "allOf": [ + { + "contains": { + "type": "object", + "properties": { + "path": { + "const": "reports/adoption_drift_report.json" + }, + "kind": { + "const": "adoption-drift-report" + } + }, + "required": [ + "path", + "kind" + ] + } + }, + { + "contains": { + "type": "object", + "properties": { + "path": { + "const": "reports/telemetry_hook_recipes.json" + }, + "kind": { + "const": "hook-recipes" + } + }, + "required": [ + "path", + "kind" + ] + } + } + ] + } + } + } + } + ], + "additionalProperties": true +} diff --git a/evidence/world_class/templates/human-adjudication.intake.json b/evidence/world_class/templates/human-adjudication.intake.json new file mode 100644 index 0000000..f6cfd72 --- /dev/null +++ b/evidence/world_class/templates/human-adjudication.intake.json @@ -0,0 +1,64 @@ +{ + "schema_version": "1.0", + "evidence_key": "human-adjudication", + "template_only": true, + "category": "human", + "source_type": "blind-ab-review", + "submitted_by": "human reviewer", + "submitted_at": "YYYY-MM-DD", + "summary": "Real blind A/B reviewer decisions recorded before the answer key was opened.", + "artifact_refs": [ + { + "path": "reports/output_review_adjudication.json", + "kind": "adjudication-report", + "contains_raw_content": false, + "note": "Must show pending_count == 0, invalid_decision_count == 0, blind_review_attested == true, and review_integrity.blind_pack_sha256." + }, + { + "path": "reports/output_review_decisions.json", + "kind": "review-decisions", + "contains_raw_content": false, + "note": "Reviewer identity, reviewed_at, winner_variant, confidence, required reason, reviewer_attestation, and review_integrity only." + } + ], + "provenance": { + "reviewer": "name or team handle", + "reviewed_at": "YYYY-MM-DD", + "blind_pack_path": "reports/output_blind_review_pack.md", + "blind_pack_sha256": "sha256 from reports/output_review_adjudication.json review_integrity.blind_pack_sha256", + "answer_key_opened_after_decisions": true, + "blind_review_completed_before_answer_key": true, + "decision_fields": [ + "case_id", + "winner_variant", + "confidence", + "reason", + "reviewer_attestation", + "review_integrity" + ], + "reviewer_reason_required": true + }, + "privacy": { + "raw_user_content_included": false, + "raw_provider_prompt_included": false, + "credentials_included": false, + "secrets_included": false, + "notes": "Reviewer reasons must be based on the visible rubric and must not include private customer data." + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "attestation": { + "real_external_or_human_evidence": false, + "reviewer_or_operator_identity_present": false, + "artifact_refs_reviewed": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "notes": "Copy this template into submissions/ after a real reviewer completes blind decisions." + } +} diff --git a/evidence/world_class/templates/native-client-telemetry.intake.json b/evidence/world_class/templates/native-client-telemetry.intake.json new file mode 100644 index 0000000..9dfdfa2 --- /dev/null +++ b/evidence/world_class/templates/native-client-telemetry.intake.json @@ -0,0 +1,53 @@ +{ + "schema_version": "1.0", + "evidence_key": "native-client-telemetry", + "template_only": true, + "category": "external", + "source_type": "native-client-telemetry", + "submitted_by": "Browser/Chrome/IDE/provider client integrator", + "submitted_at": "YYYY-MM-DD", + "summary": "Metadata-only production events imported from a real external client into the local drift loop.", + "artifact_refs": [ + { + "path": "reports/adoption_drift_report.json", + "kind": "adoption-drift-report", + "contains_raw_content": false, + "note": "Must show summary.source_types.external > 0 and adoption_sample_count > 0." + }, + { + "path": "reports/telemetry_hook_recipes.json", + "kind": "hook-recipes", + "contains_raw_content": false, + "note": "Documents local-first metadata-only client integration." + } + ], + "provenance": { + "client": "Browser/Chrome/IDE/provider client", + "native_host_manifest": "/local/path/not/committed", + "event_source": "external", + "metadata_only": true + }, + "privacy": { + "raw_user_content_included": false, + "raw_provider_prompt_included": false, + "credentials_included": false, + "secrets_included": false, + "notes": "Never commit telemetry JSONL with raw prompts, outputs, transcripts, notes, or messages." + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "attestation": { + "real_external_or_human_evidence": false, + "reviewer_or_operator_identity_present": false, + "artifact_refs_reviewed": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "notes": "Copy this template into submissions/ after real client events have been imported." + } +} diff --git a/evidence/world_class/templates/native-permission-enforcement.intake.json b/evidence/world_class/templates/native-permission-enforcement.intake.json new file mode 100644 index 0000000..e16db35 --- /dev/null +++ b/evidence/world_class/templates/native-permission-enforcement.intake.json @@ -0,0 +1,61 @@ +{ + "schema_version": "1.0", + "evidence_key": "native-permission-enforcement", + "template_only": true, + "category": "external", + "source_type": "runtime-permission-guard", + "submitted_by": "target client or installer integrator", + "submitted_at": "YYYY-MM-DD", + "summary": "A real target or installer guard enforces approved high-permission capabilities at runtime.", + "artifact_refs": [ + { + "path": "reports/runtime_permission_probes.json", + "kind": "runtime-probe-report", + "contains_raw_content": false, + "note": "Must show native_enforcement_count > 0 and failure_count == 0 for target-native completion; installer_enforcement_pass_count is supporting local evidence only." + }, + { + "path": "reports/install_simulation.json", + "kind": "installer-enforcement-report", + "contains_raw_content": false, + "note": "Shows local package installer enforcement. This does not replace target-native or external runtime guard evidence." + }, + { + "path": "dist/targets//adapter.json", + "kind": "target-adapter", + "contains_raw_content": false, + "note": "Adapter may claim native enforcement only when the target really enforces it." + } + ], + "provenance": { + "target": "openai|claude|generic|vscode|other", + "guard_location": "client or installer component", + "guard_scope": "target-client-native", + "guard_blocks_undeclared_capability": true, + "metadata_fallback_retained_for_other_targets": true, + "notes": "Use target-client-native for client-enforced guards or external-installer-runtime-guard for a real external installer guard." + }, + "privacy": { + "raw_user_content_included": false, + "raw_provider_prompt_included": false, + "credentials_included": false, + "secrets_included": false, + "notes": "Runtime guard proof should avoid environment dumps." + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "attestation": { + "real_external_or_human_evidence": false, + "reviewer_or_operator_identity_present": false, + "artifact_refs_reviewed": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "notes": "Copy this template into submissions/ only after a real client or installer guard exists." + } +} diff --git a/evidence/world_class/templates/provider-holdout.intake.json b/evidence/world_class/templates/provider-holdout.intake.json new file mode 100644 index 0000000..631982b --- /dev/null +++ b/evidence/world_class/templates/provider-holdout.intake.json @@ -0,0 +1,50 @@ +{ + "schema_version": "1.0", + "evidence_key": "provider-holdout", + "template_only": true, + "category": "external", + "source_type": "provider-output-eval", + "submitted_by": "operator with provider credentials", + "submitted_at": "YYYY-MM-DD", + "summary": "Provider-backed holdout run with model, timing, token metadata, and aggregate output hashes.", + "artifact_refs": [ + { + "path": "reports/output_execution_runs.json", + "kind": "aggregate-report", + "contains_raw_content": false, + "note": "Must show model_executed_count > 0 and token_observed_count > 0." + } + ], + "provenance": { + "provider": "", + "model": "", + "run_command": "python3 scripts/yao.py output-exec --provider-runner --provider-model --timeout-seconds 60", + "credential_env": "", + "model_env": "YAO_OUTPUT_EVAL_MODEL", + "model_default": "", + "credential_material_committed": false + }, + "privacy": { + "raw_user_content_included": false, + "raw_provider_prompt_included": false, + "credentials_included": false, + "secrets_included": false, + "notes": "Commit aggregate metadata and hashes only." + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "attestation": { + "real_external_or_human_evidence": false, + "reviewer_or_operator_identity_present": false, + "artifact_refs_reviewed": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "notes": "Copy this template into submissions/ and set all booleans truthfully after the real provider run." + } +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a7a6e2f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,41 @@ +# Examples + +This directory shows complete examples from raw workflow input to final skill package shape. + +It is organized in three tiers: + +- `simple-note-cleanup`: a small personal workflow +- `team-frontend-review`: a team-level reusable workflow +- `complex-release-orchestrator`: a multi-step, higher-complexity workflow +- `governed-incident-command`: a governed operational asset with lifecycle metadata, review cadence, and evidence reports + +Each example contains: + +- `raw-workflow.md`: what the user originally gives +- `design-summary.md`: how the meta-skill scopes and structures the result +- `generated-skill/`: the resulting skill package shape + +Some examples also include: + +- `optimization/`: example-specific baseline descriptions, semantic configs, dev/holdout suites, and route-optimization reports +- `optimization/blind_holdout/`: acceptance prompts used only after the winner is chosen on dev +- `optimization/adversarial/`: harder route-collision prompts used to measure noisy positives, deceptive negatives, and family-level calibration + +The complex example is intentionally thicker than the others and includes: + +- `manifest.json` +- `input/` and `outputs/` sample artifacts +- `evals/trigger_cases.json` +- richer references and a deterministic script + +The governed example adds: + +- explicit lifecycle and maturity metadata +- review policy and governed ownership +- reports for maturity and revision history +- context budget evidence +- a stronger example of how reusable skills become maintained assets + +Additional evolution example: + +- `evolution-frontend-review/`: shows raw workflow, v0, v1, final, and eval delta diff --git a/examples/complex-release-orchestrator/design-summary.md b/examples/complex-release-orchestrator/design-summary.md new file mode 100644 index 0000000..b24e329 --- /dev/null +++ b/examples/complex-release-orchestrator/design-summary.md @@ -0,0 +1,9 @@ +# Design Summary + +- Skill tier: complex +- Trigger surface: release prep, release checklist, rollout coordination, rollback planning +- Deterministic content: checklist stages, migration prompts, packet sections, and stakeholder message scaffolds +- Flexible content: risk commentary, go/no-go judgment, and escalation notes +- Package choice: `SKILL.md`, `agents/interface.yaml`, `manifest.json`, `references/`, `scripts/`, `evals/`, `input/`, `outputs/` +- Boundary rule: use for release packet creation and readiness review, not for general project planning or one-off announcements +- Validation goal: the generated packet must contain release scope, migration notes, rollout plan, rollback triggers, stakeholder communication, and an explicit decision block diff --git a/examples/complex-release-orchestrator/generated-skill/SKILL.md b/examples/complex-release-orchestrator/generated-skill/SKILL.md new file mode 100644 index 0000000..cf2ea37 --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/SKILL.md @@ -0,0 +1,68 @@ +--- +name: release-orchestrator +description: Coordinate software release preparation, rollout readiness, migration notes, rollback planning, stakeholder communication, and final go/no-go release packets. Use when asked to prepare a release packet, review release readiness, or turn scattered launch notes into one reusable release workflow. Do not use for generic project planning, one-off announcement drafting, or high-level release theory. +--- + +# Release Orchestrator + +## When To Use + +- You need one reusable release packet from scattered launch inputs. +- You need to verify migration notes, rollout steps, rollback triggers, and stakeholder messages before release approval. +- You need a go/no-go summary with explicit blockers. + +## Do Not Use + +- The request is only to explain a release concept. +- The request is only to write a single announcement. +- The request is still brainstorming and not yet packaging a repeatable release workflow. + +## Workflow + +1. Gather the minimum release inputs: + - release scope + - risky changes + - migrations + - rollout sequencing + - rollback triggers + - communication audiences +2. Read `references/release-checklist.md` and `references/risk-matrix.md` to determine required sections and escalation thresholds. +3. Read `references/migration-template.md` and `references/stakeholder-comms.md` when migrations or communications are present. +4. Use `scripts/build_release_packet.py` with `input/release_input_example.json` as the shape reference when deterministic packet assembly is needed. +5. Produce one release packet with: + - release summary + - dependency and migration section + - rollout steps + - rollback triggers + - stakeholder communication plan + - explicit go/no-go decision +6. If release-critical data is missing, stop and report blockers instead of pretending readiness. + +## Output Contract + +The final answer must include: + +- a concise scope summary +- a risk block with severity +- migration notes or a clear "none required" +- rollout plan +- rollback plan +- stakeholder communication block +- a final decision: `GO`, `GO WITH CONDITIONS`, or `NO-GO` + +## Validation Checklist + +- Release-critical inputs are either present or explicitly flagged as missing. +- Rollout and rollback are both covered. +- Migration notes are not omitted when schema or data changes exist. +- Stakeholder communication is tailored by audience and timing. +- The final decision is explicit and justified. + +## Reference Map + +- Read `references/release-checklist.md` for required packet sections. +- Read `references/risk-matrix.md` for decision thresholds. +- Read `references/migration-template.md` for migration note structure. +- Read `references/stakeholder-comms.md` for announcement scaffolds. +- Inspect `evals/trigger_cases.json` for trigger boundaries. +- Inspect `outputs/release_packet_example.md` for an example final artifact. diff --git a/examples/complex-release-orchestrator/generated-skill/agents/interface.yaml b/examples/complex-release-orchestrator/generated-skill/agents/interface.yaml new file mode 100644 index 0000000..943693f --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/agents/interface.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Release Orchestrator" + short_description: "Build reusable release packets with rollout, rollback, migration, and communication coverage" + default_prompt: "Use $release-orchestrator to turn these release notes into a full release packet with go/no-go guidance." diff --git a/examples/complex-release-orchestrator/generated-skill/evals/trigger_cases.json b/examples/complex-release-orchestrator/generated-skill/evals/trigger_cases.json new file mode 100644 index 0000000..4734f14 --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/evals/trigger_cases.json @@ -0,0 +1,16 @@ +{ + "recommended_threshold": 0.33, + "should_trigger": [ + "Prepare a release packet from these migration notes, rollback conditions, and rollout steps.", + "Review release readiness and produce a go/no-go packet for this launch.", + "Turn these scattered release notes into a reusable release workflow." + ], + "should_not_trigger": [ + "Explain what a rollback plan is.", + "Draft one short launch announcement for customers." + ], + "near_neighbor": [ + "Brainstorm some release ideas before we decide whether to package the workflow.", + "Summarize these release notes without creating a reusable workflow." + ] +} diff --git a/examples/complex-release-orchestrator/generated-skill/input/release_input_example.json b/examples/complex-release-orchestrator/generated-skill/input/release_input_example.json new file mode 100644 index 0000000..36401cb --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/input/release_input_example.json @@ -0,0 +1,31 @@ +{ + "release_name": "2026.03.31-search-hardening", + "scope_summary": "Adds query guardrails, dependency upgrades, and an index migration for the search service.", + "risks": [ + { + "severity": "high", + "summary": "Index migration must complete before traffic shift." + }, + { + "severity": "medium", + "summary": "Cache warm-up may temporarily increase search latency." + } + ], + "migrations": "Run search_index_v7 migration before rollout. Rollback is safe only before background reindex completion.", + "rollout_steps": [ + "Run migration in primary region.", + "Verify index health and shard count.", + "Shift 10 percent traffic and monitor search latency.", + "Shift remaining traffic after 15 minutes of stable metrics." + ], + "rollback_triggers": [ + "Search error rate above 2 percent for 5 minutes.", + "Median latency increases above 400ms after warm-up window." + ], + "stakeholder_messages": { + "engineering-oncall": "Monitor search dashboards during the first 15 minutes after traffic shift.", + "support": "Watch for search degradation tickets during the release window.", + "leadership": "Release is go-with-conditions because migration timing is critical." + }, + "decision": "GO WITH CONDITIONS" +} diff --git a/examples/complex-release-orchestrator/generated-skill/manifest.json b/examples/complex-release-orchestrator/generated-skill/manifest.json new file mode 100644 index 0000000..01e1a5b --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "release-orchestrator", + "version": "1.0.0", + "owner": "Yao Team", + "updated_at": "2026-03-31", + "status": "active", + "maturity_tier": "production", + "lifecycle_stage": "production", + "context_budget_tier": "production", + "review_cadence": "quarterly", + "category": "operations", + "complexity_tier": "complex", + "factory_components": [ + "references", + "scripts", + "evals", + "input", + "outputs", + "reports" + ] +} diff --git a/examples/complex-release-orchestrator/generated-skill/outputs/release_packet_example.md b/examples/complex-release-orchestrator/generated-skill/outputs/release_packet_example.md new file mode 100644 index 0000000..9c72471 --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/outputs/release_packet_example.md @@ -0,0 +1,29 @@ +# Release Packet: 2026.03.31-search-hardening + +## Scope +Adds query guardrails, dependency upgrades, and an index migration for the search service. + +## Risks +- [HIGH] Index migration must complete before traffic shift. +- [MEDIUM] Cache warm-up may temporarily increase search latency. + +## Migrations +Run search_index_v7 migration before rollout. Rollback is safe only before background reindex completion. + +## Rollout +- Run migration in primary region. +- Verify index health and shard count. +- Shift 10 percent traffic and monitor search latency. +- Shift remaining traffic after 15 minutes of stable metrics. + +## Rollback +- Search error rate above 2 percent for 5 minutes. +- Median latency increases above 400ms after warm-up window. + +## Stakeholder Communication +- engineering-oncall: Monitor search dashboards during the first 15 minutes after traffic shift. +- support: Watch for search degradation tickets during the release window. +- leadership: Release is go-with-conditions because migration timing is critical. + +## Final Decision +GO WITH CONDITIONS diff --git a/examples/complex-release-orchestrator/generated-skill/references/migration-template.md b/examples/complex-release-orchestrator/generated-skill/references/migration-template.md new file mode 100644 index 0000000..02841d5 --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/references/migration-template.md @@ -0,0 +1,22 @@ +# Migration Template + +Use this shape whenever the release changes schema, data contracts, or runtime configuration. + +## Required Fields + +- migration type +- affected service or datastore +- execution timing +- rollback expectation +- operator warning + +## Template + +```md +### Migration +- Type: +- Affected Surface: +- Run Before Rollout: +- Rollback Safe: +- Operator Warning: +``` diff --git a/examples/complex-release-orchestrator/generated-skill/references/release-checklist.md b/examples/complex-release-orchestrator/generated-skill/references/release-checklist.md new file mode 100644 index 0000000..e32bf55 --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/references/release-checklist.md @@ -0,0 +1,31 @@ +# Release Checklist + +## Required Sections + +- release summary +- dependency review +- migration notes +- rollout checklist +- rollback checklist +- stakeholder communication +- final go/no-go decision + +## Release Summary Questions + +- What changed in this release? +- Which services, schemas, or integrations are affected? +- What is the blast radius if rollout fails? + +## Rollout Gate + +- Sequencing is clear. +- Ownership is explicit. +- Monitoring signal is defined. +- Rollback trigger is concrete, not vague. + +## Packet Failure Modes + +- Missing migration notes when data changes exist +- Missing rollback trigger +- "Go" recommendation without acknowledged risk +- Stakeholder message missing audience or send timing diff --git a/examples/complex-release-orchestrator/generated-skill/references/risk-matrix.md b/examples/complex-release-orchestrator/generated-skill/references/risk-matrix.md new file mode 100644 index 0000000..b0cfd51 --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/references/risk-matrix.md @@ -0,0 +1,14 @@ +# Risk Matrix + +## Severity Guide + +- `low`: internal-only changes, no migrations, rollback is trivial +- `medium`: user-facing changes with limited blast radius or known manual recovery +- `high`: schema changes, cross-service dependencies, or rollback with coordination cost +- `critical`: irreversible data impact, unclear rollback, or no monitoring confidence + +## Decision Rules + +- `GO`: no unresolved high/critical risks and rollback path is clear +- `GO WITH CONDITIONS`: risks remain but owners, mitigations, and gates are explicit +- `NO-GO`: missing migration details, missing rollback criteria, or critical unknowns diff --git a/examples/complex-release-orchestrator/generated-skill/references/stakeholder-comms.md b/examples/complex-release-orchestrator/generated-skill/references/stakeholder-comms.md new file mode 100644 index 0000000..cb75bf7 --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/references/stakeholder-comms.md @@ -0,0 +1,21 @@ +# Stakeholder Communication + +## Audiences + +- engineering on-call +- support +- customer-facing operations +- internal leadership + +## Message Rules + +- state release window +- state impact surface +- state expected operator action or "no action required" +- state rollback or escalation signal when risk is non-trivial + +## Example Format + +- Engineering on-call: release window, dashboards to watch, rollback trigger +- Support: user-visible changes, expected incidents, escalation owner +- Leadership: scope, major risk, final decision diff --git a/examples/complex-release-orchestrator/generated-skill/reports/context_budget.json b/examples/complex-release-orchestrator/generated-skill/reports/context_budget.json new file mode 100644 index 0000000..660f62f --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/reports/context_budget.json @@ -0,0 +1,54 @@ +{ + "ok": true, + "failures": [], + "warnings": [], + "stats": { + "context_budget_tier": "production", + "context_budget_limit": 1000, + "skill_body_tokens": 718, + "other_text_tokens": 1842, + "estimated_initial_load_tokens": 790, + "estimated_total_text_tokens": 2560, + "deferred_resource_tokens": 1657, + "deferred_resource_warn_threshold": 120000, + "deferred_resource_dirs": [ + { + "path": "references", + "estimated_tokens": 554, + "file_count": 4 + }, + { + "path": "scripts", + "estimated_tokens": 367, + "file_count": 1 + }, + { + "path": "input", + "estimated_tokens": 315, + "file_count": 1 + }, + { + "path": "outputs", + "estimated_tokens": 263, + "file_count": 1 + }, + { + "path": "evals", + "estimated_tokens": 158, + "file_count": 1 + } + ], + "large_deferred_resource_dirs": [], + "deferred_resource_governance": { + "status": "not-required", + "large_dir_count": 0, + "governed_large_dir_count": 0, + "directories": [], + "summary": "No large deferred resource directory exceeds the per-dir threshold." + }, + "relevant_file_count": 11, + "unused_resource_dirs": [], + "quality_signal_points": 130, + "quality_density": 164.6 + } +} diff --git a/examples/complex-release-orchestrator/generated-skill/scripts/build_release_packet.py b/examples/complex-release-orchestrator/generated-skill/scripts/build_release_packet.py new file mode 100644 index 0000000..c67ef7b --- /dev/null +++ b/examples/complex-release-orchestrator/generated-skill/scripts/build_release_packet.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +import json +import sys +from pathlib import Path + + +def render_packet(payload: dict) -> str: + lines = [ + f"# Release Packet: {payload['release_name']}", + "", + "## Scope", + payload["scope_summary"], + "", + "## Risks", + ] + for risk in payload["risks"]: + lines.append(f"- [{risk['severity'].upper()}] {risk['summary']}") + lines.extend( + [ + "", + "## Migrations", + payload["migrations"], + "", + "## Rollout", + ] + ) + for step in payload["rollout_steps"]: + lines.append(f"- {step}") + lines.extend( + [ + "", + "## Rollback", + ] + ) + for trigger in payload["rollback_triggers"]: + lines.append(f"- {trigger}") + lines.extend( + [ + "", + "## Stakeholder Communication", + ] + ) + for audience, message in payload["stakeholder_messages"].items(): + lines.append(f"- {audience}: {message}") + lines.extend( + [ + "", + "## Final Decision", + payload["decision"], + ] + ) + return "\n".join(lines) + "\n" + + +def main() -> None: + if len(sys.argv) != 2: + raise SystemExit("Usage: build_release_packet.py ") + payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + print(render_packet(payload), end="") + + +if __name__ == "__main__": + main() diff --git a/examples/complex-release-orchestrator/raw-workflow.md b/examples/complex-release-orchestrator/raw-workflow.md new file mode 100644 index 0000000..461f11f --- /dev/null +++ b/examples/complex-release-orchestrator/raw-workflow.md @@ -0,0 +1,27 @@ +# Raw Workflow + +Every release we have to pull information from too many places and rebuild the same release packet by hand. + +Current inputs are scattered across: + +- merged PR summaries from the current milestone +- dependency update notes +- migration docs from backend and data teams +- rollout sequencing notes from on-call +- rollback conditions kept in an old runbook +- stakeholder announcements drafted in chat + +What usually goes wrong: + +- a migration note is missing when we think the release is ready +- rollback criteria are vague +- stakeholder communication is written too late +- someone says "ship it" before the packet has a clear go/no-go decision + +What we want from the skill: + +- ask for the minimum required release inputs +- produce one consistent release packet +- force a go/no-go summary +- call out missing release-critical details before approval +- keep deterministic artifacts separate from judgment calls diff --git a/examples/evolution-frontend-review/README.md b/examples/evolution-frontend-review/README.md new file mode 100644 index 0000000..394d5ee --- /dev/null +++ b/examples/evolution-frontend-review/README.md @@ -0,0 +1,9 @@ +# Evolution Example: Frontend Review + +This example shows a full improvement chain: + +- `raw-workflow.md`: initial request +- `v0/`: initial skill draft +- `v1/`: refined skill draft with better trigger and output contract +- `final/`: final package shape +- `eval-delta.md`: what changed and why diff --git a/examples/evolution-frontend-review/eval-delta.md b/examples/evolution-frontend-review/eval-delta.md new file mode 100644 index 0000000..3b6aa46 --- /dev/null +++ b/examples/evolution-frontend-review/eval-delta.md @@ -0,0 +1,19 @@ +# Eval Delta + +## v0 + +- Trigger too vague +- No output contract +- High under-trigger risk + +## v1 + +- Trigger became more specific +- Added review categories +- Better routing, but output still underspecified + +## final + +- Stronger trigger coverage +- Output contract added +- Review standard became reusable for team workflows diff --git a/examples/evolution-frontend-review/final/SKILL.md b/examples/evolution-frontend-review/final/SKILL.md new file mode 100644 index 0000000..850d08c --- /dev/null +++ b/examples/evolution-frontend-review/final/SKILL.md @@ -0,0 +1,22 @@ +--- +name: frontend-review +description: Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality. +--- + +# Frontend Review + +Review UI code with a pre-merge mindset. Findings come first. Focus on behavior regressions, accessibility gaps, risky form handling, unsafe rendering, and missing user states. + +## Workflow + +1. Identify the UI surface and user flows affected. +2. Check accessibility, security-sensitive inputs, loading states, and error states. +3. Report findings by severity with concrete code references. +4. Prefer actionable fixes over generic advice. + +## Output Contract + +- Start with findings, ordered by severity. +- Include file references for each finding. +- Call out open questions separately from confirmed issues. +- If there are no findings, say that explicitly and note residual risks or test gaps. diff --git a/examples/evolution-frontend-review/raw-workflow.md b/examples/evolution-frontend-review/raw-workflow.md new file mode 100644 index 0000000..ea5c2e8 --- /dev/null +++ b/examples/evolution-frontend-review/raw-workflow.md @@ -0,0 +1,10 @@ +# Raw Workflow + +Review our frontend code before merge for: + +- accessibility issues +- risky HTML rendering +- missing loading and error states +- bad form behavior + +We want a reusable review skill, not a one-off checklist. diff --git a/examples/evolution-frontend-review/v0/SKILL.md b/examples/evolution-frontend-review/v0/SKILL.md new file mode 100644 index 0000000..6e861f0 --- /dev/null +++ b/examples/evolution-frontend-review/v0/SKILL.md @@ -0,0 +1,11 @@ +--- +name: frontend-review +description: Review frontend code before merge. +--- + +# Frontend Review + +## Workflow + +1. Review the code. +2. Report issues. diff --git a/examples/evolution-frontend-review/v1/SKILL.md b/examples/evolution-frontend-review/v1/SKILL.md new file mode 100644 index 0000000..a7efff9 --- /dev/null +++ b/examples/evolution-frontend-review/v1/SKILL.md @@ -0,0 +1,14 @@ +--- +name: frontend-review +description: Review frontend code for accessibility, risky UI security issues, missing loading and error states, and pre-merge regressions. Use when asked to review React, UI, forms, frontend components, or merge-readiness for the frontend. +--- + +# Frontend Review + +Review UI code with a pre-merge mindset. + +## Workflow + +1. Identify affected flows. +2. Check accessibility, rendering safety, form behavior, loading states, and error states. +3. Report findings by severity with code references. diff --git a/examples/governed-incident-command/design-summary.md b/examples/governed-incident-command/design-summary.md new file mode 100644 index 0000000..c83f200 --- /dev/null +++ b/examples/governed-incident-command/design-summary.md @@ -0,0 +1,30 @@ +# Design Summary + +This example is intentionally governed rather than merely complex. + +## Scope + +The skill exists to assemble reusable incident command packets from messy operational artifacts. + +It does not exist for: + +- generic debugging help +- one-off status message drafting +- incident theory or training-only explanations + +## Boundary Decisions + +- Severity policy moved to `references/severity-matrix.md` +- stakeholder messaging moved to `references/comms-policy.md` +- monthly review expectations moved to `references/review-policy.md` +- deterministic output shaping moved to `scripts/build_incident_packet.py` +- a governed `manifest.json` is required because this skill is intended to be reviewed and maintained + +## Why This Is A Governed Example + +- explicit owner +- monthly review cadence +- active lifecycle state +- governed maturity tier +- reports and evidence files +- trigger evals plus sample output artifact diff --git a/examples/governed-incident-command/generated-skill/SKILL.md b/examples/governed-incident-command/generated-skill/SKILL.md new file mode 100644 index 0000000..d3d450e --- /dev/null +++ b/examples/governed-incident-command/generated-skill/SKILL.md @@ -0,0 +1,70 @@ +--- +name: incident-command-governor +description: Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. +--- + +# Incident Command Governor + +## When To Use + +- You need a reusable incident command packet from messy operational inputs. +- You need explicit severity assessment, stakeholder updates, and action ownership. +- You need a governed workflow that can be reviewed, maintained, and audited over time. + +## Do Not Use + +- The task is only to explain an incident concept. +- The task is only to draft a one-off update. +- The request is still brainstorming and not yet packaging a repeatable incident workflow. + +## Workflow + +1. Gather the minimum incident inputs: + - incident summary + - timeline evidence + - affected systems and customers + - severity indicators + - owners and responders + - stakeholder audiences +2. Read `references/severity-matrix.md` to assign or confirm severity. +3. Read `references/comms-policy.md` to shape internal and external updates. +4. Read `references/review-policy.md` before finalizing, because this skill is maintained under a monthly review cadence. +5. Use `scripts/build_incident_packet.py` with `input/incident_input_example.json` when deterministic packet assembly is needed. +6. Produce one governed incident packet with: + - incident summary + - severity block + - evidence-backed timeline + - affected scope + - owner and action table + - internal and external comms blocks + - explicit open risks and review notes +7. Stop and report blockers if severity evidence or ownership data is missing. + +## Output Contract + +The final answer must include: + +- a concise incident summary +- explicit severity and rationale +- a timeline section +- impact scope +- owners and next actions +- stakeholder communication blocks +- governance note for follow-up review + +## Validation Checklist + +- Severity is justified with evidence, not guessed. +- Owner and next actions are explicit. +- Communications are separated by audience. +- Missing evidence is surfaced as a blocker. +- The packet can be reviewed again without reconstructing context from chat history. + +## Reference Map + +- Read `references/severity-matrix.md` for severity rules. +- Read `references/comms-policy.md` for communication guidance. +- Read `references/review-policy.md` for review cadence and governed lifecycle expectations. +- Inspect `evals/trigger_cases.json` for routing boundaries. +- Inspect `outputs/incident_packet_example.md` for an example final artifact. +- Inspect `reports/governance_score.json` for the current maturity snapshot. diff --git a/examples/governed-incident-command/generated-skill/agents/interface.yaml b/examples/governed-incident-command/generated-skill/agents/interface.yaml new file mode 100644 index 0000000..b5d8e86 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/agents/interface.yaml @@ -0,0 +1,10 @@ +interface: + display_name: "Incident Command Governor" + short_description: "Build governed incident packets with severity, ownership, communication, and review coverage" + default_prompt: "Use $incident-command-governor to turn these incident artifacts into a governed incident command packet." +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "openai" + - "claude" + - "generic" diff --git a/examples/governed-incident-command/generated-skill/evals/trigger_cases.json b/examples/governed-incident-command/generated-skill/evals/trigger_cases.json new file mode 100644 index 0000000..be0a0b1 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/evals/trigger_cases.json @@ -0,0 +1,14 @@ +{ + "should_trigger": [ + "Turn these incident notes and responder transcripts into a governed incident command packet.", + "Create a reusable incident review skill from this outage workflow." + ], + "should_not_trigger": [ + "Explain what incident severity means.", + "Draft one short outage update only." + ], + "near_neighbor": [ + "Summarize this incident but do not package it as a reusable skill.", + "Help brainstorm improvements to incident handling, but do not build the governed workflow yet." + ] +} diff --git a/examples/governed-incident-command/generated-skill/input/incident_input_example.json b/examples/governed-incident-command/generated-skill/input/incident_input_example.json new file mode 100644 index 0000000..b79137b --- /dev/null +++ b/examples/governed-incident-command/generated-skill/input/incident_input_example.json @@ -0,0 +1,24 @@ +{ + "incident_summary": "Primary API latency spiked after a rollout and customer dashboard requests timed out for 14 minutes.", + "timeline": [ + "09:02 deploy started", + "09:05 error rate increased", + "09:09 rollback decision made", + "09:16 latency returned to baseline" + ], + "affected_scope": "Dashboard API and alert notifications", + "severity_signals": [ + "customer-facing outage", + "elevated error rate", + "rollback required" + ], + "owners": [ + "API on-call", + "release manager" + ], + "stakeholders": [ + "incident responders", + "leadership", + "customer success" + ] +} diff --git a/examples/governed-incident-command/generated-skill/manifest.json b/examples/governed-incident-command/generated-skill/manifest.json new file mode 100644 index 0000000..7bb56d3 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/manifest.json @@ -0,0 +1,26 @@ +{ + "name": "incident-command-governor", + "version": "1.0.0", + "owner": "Incident Operations Team", + "updated_at": "2026-03-31", + "status": "active", + "maturity_tier": "governed", + "lifecycle_stage": "governed", + "context_budget_tier": "production", + "review_cadence": "monthly", + "category": "operations", + "complexity_tier": "complex", + "target_platforms": [ + "openai", + "claude", + "generic" + ], + "factory_components": [ + "references", + "scripts", + "evals", + "input", + "outputs", + "reports" + ] +} diff --git a/examples/governed-incident-command/generated-skill/outputs/incident_packet_example.md b/examples/governed-incident-command/generated-skill/outputs/incident_packet_example.md new file mode 100644 index 0000000..0eae167 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/outputs/incident_packet_example.md @@ -0,0 +1,27 @@ +# Incident Command Packet + +## Summary +Primary API latency spiked after a rollout and customer dashboard requests timed out for 14 minutes. + +## Severity +SEV1, because the event was customer-facing and required rollback. + +## Timeline +- 09:02 deploy started +- 09:05 error rate increased +- 09:09 rollback decision made +- 09:16 latency returned to baseline + +## Impact Scope +Dashboard API and alert notifications + +## Owners And Actions +- API on-call: verify no residual latency +- Release manager: confirm rollback completeness + +## Communications +- Internal: responders and leadership receive explicit timeline plus next checkpoint +- External: customer-facing update references impact, mitigation, and next status time + +## Governance Note +Review packet structure and severity policy at the next monthly governance review. diff --git a/examples/governed-incident-command/generated-skill/references/comms-policy.md b/examples/governed-incident-command/generated-skill/references/comms-policy.md new file mode 100644 index 0000000..a4ee9a6 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/references/comms-policy.md @@ -0,0 +1,9 @@ +# Communications Policy + +Separate communication by audience: + +- internal responders: timeline, mitigations, blockers +- leadership: impact, severity, owner, next checkpoint +- external stakeholders: impact, current mitigation, next update time + +Do not reuse internal wording for external updates without simplification and scope control. diff --git a/examples/governed-incident-command/generated-skill/references/review-policy.md b/examples/governed-incident-command/generated-skill/references/review-policy.md new file mode 100644 index 0000000..56b8443 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/references/review-policy.md @@ -0,0 +1,14 @@ +# Review Policy + +This skill is a governed operational asset. + +- owner: Incident Operations Team +- review cadence: monthly +- maturity tier: governed +- required drift signals: + - trigger regression changes + - severity-policy changes + - communication-template changes + - output-shape changes + +If policy or escalation routes change, update the references and rerun governance plus trigger checks. diff --git a/examples/governed-incident-command/generated-skill/references/severity-matrix.md b/examples/governed-incident-command/generated-skill/references/severity-matrix.md new file mode 100644 index 0000000..0abd7b8 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/references/severity-matrix.md @@ -0,0 +1,7 @@ +# Severity Matrix + +- `SEV1`: customer-facing outage or severe data integrity risk +- `SEV2`: major degradation with partial customer impact +- `SEV3`: localized degradation or operational risk without broad impact + +Use the highest supported severity. If the evidence is incomplete, report uncertainty instead of downgrading silently. diff --git a/examples/governed-incident-command/generated-skill/reports/context_budget.json b/examples/governed-incident-command/generated-skill/reports/context_budget.json new file mode 100644 index 0000000..c0471b8 --- /dev/null +++ b/examples/governed-incident-command/generated-skill/reports/context_budget.json @@ -0,0 +1,54 @@ +{ + "ok": true, + "failures": [], + "warnings": [], + "stats": { + "context_budget_tier": "production", + "context_budget_limit": 1000, + "skill_body_tokens": 658, + "other_text_tokens": 1499, + "estimated_initial_load_tokens": 760, + "estimated_total_text_tokens": 2157, + "deferred_resource_tokens": 1030, + "deferred_resource_warn_threshold": 120000, + "deferred_resource_dirs": [ + { + "path": "scripts", + "estimated_tokens": 278, + "file_count": 1 + }, + { + "path": "references", + "estimated_tokens": 266, + "file_count": 3 + }, + { + "path": "outputs", + "estimated_tokens": 203, + "file_count": 1 + }, + { + "path": "input", + "estimated_tokens": 153, + "file_count": 1 + }, + { + "path": "evals", + "estimated_tokens": 130, + "file_count": 1 + } + ], + "large_deferred_resource_dirs": [], + "deferred_resource_governance": { + "status": "not-required", + "large_dir_count": 0, + "governed_large_dir_count": 0, + "directories": [], + "summary": "No large deferred resource directory exceeds the per-dir threshold." + }, + "relevant_file_count": 12, + "unused_resource_dirs": [], + "quality_signal_points": 130, + "quality_density": 171.1 + } +} diff --git a/examples/governed-incident-command/generated-skill/reports/governance_score.json b/examples/governed-incident-command/generated-skill/reports/governance_score.json new file mode 100644 index 0000000..257af3f --- /dev/null +++ b/examples/governed-incident-command/generated-skill/reports/governance_score.json @@ -0,0 +1,25 @@ +{ + "ok": true, + "failures": [], + "warnings": [], + "details": { + "skill_dir": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/examples/governed-incident-command/generated-skill", + "manifest_present": true, + "frontmatter_name": "incident-command-governor", + "manifest_name": "incident-command-governor", + "status": "active", + "maturity_tier": "governed", + "review_cadence": "monthly", + "governance_score": 95, + "declared_maturity_minimum": 90, + "computed_governance_band": "governed", + "computed_maturity_tier": "governed", + "score_breakdown": { + "metadata_integrity": 20, + "ownership_and_review": 20, + "boundary_and_eval": 25, + "operational_assets": 20, + "maintenance_evidence": 10 + } + } +} diff --git a/examples/governed-incident-command/generated-skill/reports/revision_history.md b/examples/governed-incident-command/generated-skill/reports/revision_history.md new file mode 100644 index 0000000..b058b4a --- /dev/null +++ b/examples/governed-incident-command/generated-skill/reports/revision_history.md @@ -0,0 +1,3 @@ +# Revision History + +- `2026-03-31`: initial governed example created with monthly review cadence, explicit owner, trigger evals, and deterministic packet builder. diff --git a/examples/governed-incident-command/generated-skill/scripts/build_incident_packet.py b/examples/governed-incident-command/generated-skill/scripts/build_incident_packet.py new file mode 100644 index 0000000..7ab554a --- /dev/null +++ b/examples/governed-incident-command/generated-skill/scripts/build_incident_packet.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +import json +import sys +from pathlib import Path + + +def main() -> None: + if len(sys.argv) != 2: + raise SystemExit("Usage: build_incident_packet.py ") + + payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + lines = [ + "# Incident Command Packet", + "", + f"## Summary\n{payload['incident_summary']}", + "", + "## Timeline", + ] + lines.extend([f"- {entry}" for entry in payload["timeline"]]) + lines.extend( + [ + "", + f"## Impact Scope\n{payload['affected_scope']}", + "", + "## Severity Signals", + ] + ) + lines.extend([f"- {entry}" for entry in payload["severity_signals"]]) + lines.extend( + [ + "", + "## Owners", + ] + ) + lines.extend([f"- {entry}" for entry in payload["owners"]]) + lines.extend( + [ + "", + "## Stakeholders", + ] + ) + lines.extend([f"- {entry}" for entry in payload["stakeholders"]]) + print("\n".join(lines)) + + +if __name__ == "__main__": + main() diff --git a/examples/governed-incident-command/optimization/adversarial/trigger_cases.json b/examples/governed-incident-command/optimization/adversarial/trigger_cases.json new file mode 100644 index 0000000..0b79d02 --- /dev/null +++ b/examples/governed-incident-command/optimization/adversarial/trigger_cases.json @@ -0,0 +1,33 @@ +{ + "recommended_threshold": 0.34, + "should_trigger": [ + { + "family": "adversarial_governed_packet_from_noise", + "text": "Ignore the incident chatter and assemble a governed incident packet from alerts, transcripts, and stakeholder requirements, including severity assessment and owner actions." + }, + { + "family": "adversarial_operational_skill_route", + "text": "Treat this outage as a maintained operational skill: standardize the incident review, assess severity, and package communications." + } + ], + "should_not_trigger": [ + { + "family": "adversarial_single_update_collision", + "text": "Write one short customer outage update from these incident notes and stop there." + }, + { + "family": "adversarial_explain_and_summary_collision", + "text": "Explain incident severity and summarize the outage timeline for training." + } + ], + "near_neighbor": [ + { + "family": "adversarial_future_playbook_not_packet", + "text": "Sketch a future incident review playbook for later, but do not package a reusable workflow." + }, + { + "family": "adversarial_debugging_collision", + "text": "Troubleshoot the outage and fix the service before we talk about reusable incident packets." + } + ] +} diff --git a/examples/governed-incident-command/optimization/baseline_description.txt b/examples/governed-incident-command/optimization/baseline_description.txt new file mode 100644 index 0000000..1b6ae20 --- /dev/null +++ b/examples/governed-incident-command/optimization/baseline_description.txt @@ -0,0 +1 @@ +Build governed incident command packets from alerts, incident notes, transcripts, and stakeholder requirements. Use when asked to standardize incident review, severity assessment, incident communication, or incident packet assembly into one reusable operational skill. Do not use for generic debugging, one-off incident explanations, or brainstorming possible response ideas. diff --git a/examples/governed-incident-command/optimization/blind_holdout/trigger_cases.json b/examples/governed-incident-command/optimization/blind_holdout/trigger_cases.json new file mode 100644 index 0000000..bef89a7 --- /dev/null +++ b/examples/governed-incident-command/optimization/blind_holdout/trigger_cases.json @@ -0,0 +1,33 @@ +{ + "recommended_threshold": 0.48, + "should_trigger": [ + { + "text": "Turn these alert logs and responder notes into a governed incident packet with severity rationale and owner actions.", + "family": "blind_packet_assembly" + }, + { + "text": "Standardize this outage review, run severity assessment, and assemble incident communication into a reusable incident workflow.", + "family": "blind_governed_workflow" + } + ], + "should_not_trigger": [ + { + "text": "Explain how severity levels are decided during incidents.", + "family": "blind_explain_only" + }, + { + "text": "Write a single incident update for customers from these notes.", + "family": "blind_one_off_update" + } + ], + "near_neighbor": [ + { + "text": "Summarize the outage timeline, but do not package a governed workflow.", + "family": "blind_summary_only" + }, + { + "text": "Brainstorm how incident handling could improve next quarter without building the packet.", + "family": "blind_brainstorm_only" + } + ] +} diff --git a/examples/governed-incident-command/optimization/dev/trigger_cases.json b/examples/governed-incident-command/optimization/dev/trigger_cases.json new file mode 100644 index 0000000..6f62ad6 --- /dev/null +++ b/examples/governed-incident-command/optimization/dev/trigger_cases.json @@ -0,0 +1,41 @@ +{ + "recommended_threshold": 0.48, + "should_trigger": [ + { + "text": "Turn these alerts, incident notes, and responder transcripts into a governed incident command packet.", + "family": "packet_assembly" + }, + { + "text": "Create a reusable incident review skill from this outage workflow with severity assessment and stakeholder communication.", + "family": "governed_workflow" + }, + { + "text": "Package this incident process into a maintained operational skill with severity, owners, and communication blocks.", + "family": "governed_workflow" + } + ], + "should_not_trigger": [ + { + "text": "Explain what incident severity means.", + "family": "explain_only" + }, + { + "text": "Draft one short outage update for customers.", + "family": "one_off_update" + }, + { + "text": "Debug the service outage and find the root cause.", + "family": "generic_debugging" + } + ], + "near_neighbor": [ + { + "text": "Summarize this incident but do not package it as a reusable skill.", + "family": "summary_only" + }, + { + "text": "Help brainstorm improvements to incident handling, but do not build the governed workflow yet.", + "family": "brainstorm_only" + } + ] +} diff --git a/examples/governed-incident-command/optimization/holdout/trigger_cases.json b/examples/governed-incident-command/optimization/holdout/trigger_cases.json new file mode 100644 index 0000000..98c5a24 --- /dev/null +++ b/examples/governed-incident-command/optimization/holdout/trigger_cases.json @@ -0,0 +1,33 @@ +{ + "recommended_threshold": 0.48, + "should_trigger": [ + { + "text": "Standardize this incident review process into a governed packet workflow with severity rationale and stakeholder updates.", + "family": "governed_workflow" + }, + { + "text": "Build a reusable incident command skill from alerts, timelines, and responder notes.", + "family": "packet_assembly" + } + ], + "should_not_trigger": [ + { + "text": "Write a single executive summary of this outage only.", + "family": "summary_only" + }, + { + "text": "Brainstorm what the team could do better next time.", + "family": "brainstorm_only" + } + ], + "near_neighbor": [ + { + "text": "Draft a one-off internal status update from these incident notes.", + "family": "one_off_update" + }, + { + "text": "Explain how incident command works at a high level.", + "family": "explain_only" + } + ] +} diff --git a/examples/governed-incident-command/optimization/reports/description_optimization.json b/examples/governed-incident-command/optimization/reports/description_optimization.json new file mode 100644 index 0000000..8707247 --- /dev/null +++ b/examples/governed-incident-command/optimization/reports/description_optimization.json @@ -0,0 +1,6261 @@ +{ + "current_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "current_candidate": { + "id": "current", + "label": "Current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "strategy": "current", + "estimated_tokens": 37, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 37 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "baseline": { + "description": "Build governed incident command packets from alerts, incident notes, transcripts, and stakeholder requirements. Use when asked to standardize incident review, severity assessment, incident communication, or incident packet assembly into one reusable operational skill. Do not use for generic debugging, one-off incident explanations, or brainstorming possible response ideas.", + "estimated_tokens": 93, + "dev": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "governed_workflow": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "generic_debugging": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alerts, incident notes, and responder transcripts into a governed incident command packet.", + "family": "packet_assembly", + "score": 0.686, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_inputs", + "incident_packet" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.62, + "support_score": 0, + "lexical_support": 0.692, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed" + ], + "incident_inputs": [ + "incident notes", + "alerts", + "transcripts" + ], + "incident_packet": [ + "incident command packet" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Create a reusable incident review skill from this outage workflow with severity assessment and stakeholder communication.", + "family": "governed_workflow", + "score": 0.791, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "incident_inputs", + "incident_packet", + "reusable_packaging", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.74, + "support_score": 0, + "lexical_support": 0.625, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "outage workflow" + ], + "incident_packet": [ + "incident review skill", + "incident review" + ], + "reusable_packaging": [ + "reusable", + "workflow", + "skill" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "incident review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Package this incident process into a maintained operational skill with severity, owners, and communication blocks.", + "family": "governed_workflow", + "score": 0.539, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "reusable_packaging", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.48, + "support_score": 0, + "lexical_support": 0.467, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owners" + ], + "governed_workflow": [ + "maintained", + "operational skill" + ], + "reusable_packaging": [ + "skill", + "package" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Explain what incident severity means.", + "family": "explain_only", + "score": 0.198, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.18, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Draft one short outage update for customers.", + "family": "one_off_update", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "one_off_update" + ], + "exclusive_negative_concepts": [ + "one_off_update" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.34, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "one_off_update": [ + "draft one short outage update" + ] + } + } + } + }, + { + "prompt": "Debug the service outage and find the root cause.", + "family": "generic_debugging", + "score": 0.01, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Summarize this incident but do not package it as a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "reusable_packaging" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.417, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reusable_packaging": [ + "reusable", + "skill", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Help brainstorm improvements to incident handling, but do not build the governed workflow yet.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "governed_workflow", + "reusable_packaging" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.14, + "support_score": 0, + "lexical_support": 0.429, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "reusable_packaging": [ + "workflow" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + } + }, + "holdout": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 1, + "pass_rate": 0.5 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "packet_assembly": { + "total": 1, + "passed": 0, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "packet_assembly", + "prompt": "Build a reusable incident command skill from alerts, timelines, and responder notes.", + "score": 0.321, + "reason": "false_negative", + "matched_desired_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Standardize this incident review process into a governed packet workflow with severity rationale and stakeholder updates.", + "family": "governed_workflow", + "score": 0.694, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet", + "reusable_packaging", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.64, + "support_score": 0, + "lexical_support": 0.562, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "standardize" + ], + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "workflow" + ], + "severity_assessment": [ + "severity", + "incident review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Build a reusable incident command skill from alerts, timelines, and responder notes.", + "family": "packet_assembly", + "score": 0.321, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.75, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "alerts" + ], + "reusable_packaging": [ + "reusable", + "skill" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Write a single executive summary of this outage only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + }, + { + "prompt": "Brainstorm what the team could do better next time.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.111, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a one-off internal status update from these incident notes.", + "family": "one_off_update", + "score": 0.22, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "incident_inputs" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.455, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "incident notes" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain how incident command works at a high level.", + "family": "explain_only", + "score": 0.018, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ] + } + } + }, + "winner": { + "id": "current", + "label": "Current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "strategy": "current", + "estimated_tokens": 37, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 37 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "winner_dev_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "governed_workflow": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "generic_debugging": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alerts, incident notes, and responder transcripts into a governed incident command packet.", + "family": "packet_assembly", + "score": 0.617, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet" + ], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.553, + "support_score": 0.2, + "lexical_support": 0.231, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed" + ], + "incident_inputs": [ + "incident notes", + "alerts", + "transcripts" + ], + "incident_packet": [ + "incident command packet" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Create a reusable incident review skill from this outage workflow with severity assessment and stakeholder communication.", + "family": "governed_workflow", + "score": 0.73, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.658, + "support_score": 0.24, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "outage workflow" + ], + "incident_packet": [ + "incident review skill", + "incident review" + ], + "reusable_packaging": [ + "reusable", + "workflow", + "skill" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "incident review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Package this incident process into a maintained operational skill with severity, owners, and communication blocks.", + "family": "governed_workflow", + "score": 0.619, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.579, + "support_score": 0.04, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owners" + ], + "governed_workflow": [ + "maintained", + "operational skill" + ], + "reusable_packaging": [ + "skill", + "package" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain what incident severity means.", + "family": "explain_only", + "score": 0.25, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.237, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Draft one short outage update for customers.", + "family": "one_off_update", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "one_off_update" + ], + "exclusive_negative_concepts": [ + "one_off_update" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.34, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "one_off_update": [ + "draft one short outage update" + ] + } + } + } + }, + { + "prompt": "Debug the service outage and find the root cause.", + "family": "generic_debugging", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Summarize this incident but do not package it as a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0.04, + "lexical_support": 0.083, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reusable_packaging": [ + "reusable", + "skill", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Help brainstorm improvements to incident handling, but do not build the governed workflow yet.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.132, + "support_score": 0.04, + "lexical_support": 0.286, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "reusable_packaging": [ + "workflow" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + } + }, + "winner_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 1, + "pass_rate": 0.5 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "packet_assembly": { + "total": 1, + "passed": 0, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "packet_assembly", + "prompt": "Build a reusable incident command skill from alerts, timelines, and responder notes.", + "score": 0.08, + "reason": "false_negative", + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Standardize this incident review process into a governed packet workflow with severity rationale and stakeholder updates.", + "family": "governed_workflow", + "score": 0.821, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.789, + "support_score": 0.04, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "standardize" + ], + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "workflow" + ], + "severity_assessment": [ + "severity", + "incident review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Build a reusable incident command skill from alerts, timelines, and responder notes.", + "family": "packet_assembly", + "score": 0.08, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0.24, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "alerts" + ], + "reusable_packaging": [ + "reusable", + "skill" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Write a single executive summary of this outage only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + }, + { + "prompt": "Brainstorm what the team could do better next time.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a one-off internal status update from these incident notes.", + "family": "one_off_update", + "score": 0.057, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0.2, + "lexical_support": 0.091, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "incident notes" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain how incident command works at a high level.", + "family": "explain_only", + "score": 0.018, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ] + } + }, + "current_dev_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "governed_workflow": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "generic_debugging": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alerts, incident notes, and responder transcripts into a governed incident command packet.", + "family": "packet_assembly", + "score": 0.617, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet" + ], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.553, + "support_score": 0.2, + "lexical_support": 0.231, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed" + ], + "incident_inputs": [ + "incident notes", + "alerts", + "transcripts" + ], + "incident_packet": [ + "incident command packet" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Create a reusable incident review skill from this outage workflow with severity assessment and stakeholder communication.", + "family": "governed_workflow", + "score": 0.73, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.658, + "support_score": 0.24, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "outage workflow" + ], + "incident_packet": [ + "incident review skill", + "incident review" + ], + "reusable_packaging": [ + "reusable", + "workflow", + "skill" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "incident review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Package this incident process into a maintained operational skill with severity, owners, and communication blocks.", + "family": "governed_workflow", + "score": 0.619, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.579, + "support_score": 0.04, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owners" + ], + "governed_workflow": [ + "maintained", + "operational skill" + ], + "reusable_packaging": [ + "skill", + "package" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain what incident severity means.", + "family": "explain_only", + "score": 0.25, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.237, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Draft one short outage update for customers.", + "family": "one_off_update", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "one_off_update" + ], + "exclusive_negative_concepts": [ + "one_off_update" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.34, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "one_off_update": [ + "draft one short outage update" + ] + } + } + } + }, + { + "prompt": "Debug the service outage and find the root cause.", + "family": "generic_debugging", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Summarize this incident but do not package it as a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0.04, + "lexical_support": 0.083, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reusable_packaging": [ + "reusable", + "skill", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Help brainstorm improvements to incident handling, but do not build the governed workflow yet.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.132, + "support_score": 0.04, + "lexical_support": 0.286, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "reusable_packaging": [ + "workflow" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + } + }, + "current_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 1, + "pass_rate": 0.5 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "packet_assembly": { + "total": 1, + "passed": 0, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "packet_assembly", + "prompt": "Build a reusable incident command skill from alerts, timelines, and responder notes.", + "score": 0.08, + "reason": "false_negative", + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Standardize this incident review process into a governed packet workflow with severity rationale and stakeholder updates.", + "family": "governed_workflow", + "score": 0.821, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.789, + "support_score": 0.04, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "standardize" + ], + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "workflow" + ], + "severity_assessment": [ + "severity", + "incident review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Build a reusable incident command skill from alerts, timelines, and responder notes.", + "family": "packet_assembly", + "score": 0.08, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0.24, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "alerts" + ], + "reusable_packaging": [ + "reusable", + "skill" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Write a single executive summary of this outage only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + }, + { + "prompt": "Brainstorm what the team could do better next time.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a one-off internal status update from these incident notes.", + "family": "one_off_update", + "score": 0.057, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0.2, + "lexical_support": 0.091, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "incident notes" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain how incident command works at a high level.", + "family": "explain_only", + "score": 0.018, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ] + } + }, + "winner_blind_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alert logs and responder notes into a governed incident packet with severity rationale and owner actions.", + "family": "blind_packet_assembly", + "score": 0.994, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 1.0, + "support_score": 0, + "lexical_support": 0.176, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Standardize this outage review, run severity assessment, and assemble incident communication into a reusable incident workflow.", + "family": "blind_governed_workflow", + "score": 0.645, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.579, + "support_score": 0.04, + "lexical_support": 0.533, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "incident communication" + ], + "governed_workflow": [ + "standardize" + ], + "reusable_packaging": [ + "reusable", + "workflow" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "outage review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain how severity levels are decided during incidents.", + "family": "blind_explain_only", + "score": 0.228, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.237, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Write a single incident update for customers from these notes.", + "family": "blind_one_off_update", + "score": 0.008, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Summarize the outage timeline, but do not package a governed workflow.", + "family": "blind_summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow" + ], + "extra_positive_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.132, + "support_score": 0.24, + "lexical_support": 0.091, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "incident_inputs": [ + "timeline" + ], + "reusable_packaging": [ + "workflow", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Brainstorm how incident handling could improve next quarter without building the packet.", + "family": "blind_brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + } + }, + "current_blind_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alert logs and responder notes into a governed incident packet with severity rationale and owner actions.", + "family": "blind_packet_assembly", + "score": 0.994, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 1.0, + "support_score": 0, + "lexical_support": 0.176, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Standardize this outage review, run severity assessment, and assemble incident communication into a reusable incident workflow.", + "family": "blind_governed_workflow", + "score": 0.645, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.579, + "support_score": 0.04, + "lexical_support": 0.533, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "incident communication" + ], + "governed_workflow": [ + "standardize" + ], + "reusable_packaging": [ + "reusable", + "workflow" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "outage review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain how severity levels are decided during incidents.", + "family": "blind_explain_only", + "score": 0.228, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.237, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Write a single incident update for customers from these notes.", + "family": "blind_one_off_update", + "score": 0.008, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Summarize the outage timeline, but do not package a governed workflow.", + "family": "blind_summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow" + ], + "extra_positive_concepts": [ + "incident_inputs", + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.132, + "support_score": 0.24, + "lexical_support": 0.091, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "incident_inputs": [ + "timeline" + ], + "reusable_packaging": [ + "workflow", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Brainstorm how incident handling could improve next quarter without building the packet.", + "family": "blind_brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + } + }, + "baseline_blind_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alert logs and responder notes into a governed incident packet with severity rationale and owner actions.", + "family": "blind_packet_assembly", + "score": 0.792, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.76, + "support_score": 0, + "lexical_support": 0.412, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Standardize this outage review, run severity assessment, and assemble incident communication into a reusable incident workflow.", + "family": "blind_governed_workflow", + "score": 0.55, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "reusable_packaging", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.48, + "support_score": 0, + "lexical_support": 0.6, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "incident communication" + ], + "governed_workflow": [ + "standardize" + ], + "reusable_packaging": [ + "reusable", + "workflow" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "outage review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Explain how severity levels are decided during incidents.", + "family": "blind_explain_only", + "score": 0.176, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.18, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Write a single incident update for customers from these notes.", + "family": "blind_one_off_update", + "score": 0.032, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Summarize the outage timeline, but do not package a governed workflow.", + "family": "blind_summary_only", + "score": 0.005, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_inputs", + "reusable_packaging" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.34, + "support_score": 0, + "lexical_support": 0.273, + "negative_penalty": 0.24, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "incident_inputs": [ + "timeline" + ], + "reusable_packaging": [ + "workflow", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Brainstorm how incident handling could improve next quarter without building the packet.", + "family": "blind_brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.167, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + } + }, + "winner_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alert logs and responder notes into a governed incident packet with severity rationale and owner actions.", + "family": "blind_packet_assembly", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "strong_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Standardize this outage review, run severity assessment, and assemble incident communication into a reusable incident workflow.", + "family": "blind_governed_workflow", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "strong_positive_concepts": [ + "severity_assessment", + "comms_and_owners" + ], + "trigger_action_hits": [ + "run severity assessment", + "assemble incident communication" + ], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 6, + "negative_vote": 0, + "margin": 6, + "confidence": 1.0, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "incident communication" + ], + "governed_workflow": [ + "standardize" + ], + "reusable_packaging": [ + "reusable", + "workflow" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "outage review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain how severity levels are decided during incidents.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "severity_assessment" + ], + "strong_positive_concepts": [ + "severity_assessment" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain" + ], + "positive_vote": 1, + "negative_vote": 1, + "margin": 0, + "confidence": 0.5, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Write a single incident update for customers from these notes.", + "family": "blind_one_off_update", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 0, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": {} + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Summarize the outage timeline, but do not package a governed workflow.", + "family": "blind_summary_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "governed_workflow" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "summarize" + ], + "positive_vote": 1, + "negative_vote": 3, + "margin": -2, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "incident_inputs": [ + "timeline" + ], + "reusable_packaging": [ + "workflow", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Brainstorm how incident handling could improve next quarter without building the packet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "brainstorm" + ], + "positive_vote": 1, + "negative_vote": 3, + "margin": -2, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "current_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alert logs and responder notes into a governed incident packet with severity rationale and owner actions.", + "family": "blind_packet_assembly", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "strong_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Standardize this outage review, run severity assessment, and assemble incident communication into a reusable incident workflow.", + "family": "blind_governed_workflow", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "strong_positive_concepts": [ + "severity_assessment", + "comms_and_owners" + ], + "trigger_action_hits": [ + "run severity assessment", + "assemble incident communication" + ], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 6, + "negative_vote": 0, + "margin": 6, + "confidence": 1.0, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "incident communication" + ], + "governed_workflow": [ + "standardize" + ], + "reusable_packaging": [ + "reusable", + "workflow" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "outage review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain how severity levels are decided during incidents.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "severity_assessment" + ], + "strong_positive_concepts": [ + "severity_assessment" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain" + ], + "positive_vote": 1, + "negative_vote": 1, + "margin": 0, + "confidence": 0.5, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Write a single incident update for customers from these notes.", + "family": "blind_one_off_update", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 0, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": {} + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Summarize the outage timeline, but do not package a governed workflow.", + "family": "blind_summary_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "governed_workflow" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "summarize" + ], + "positive_vote": 1, + "negative_vote": 3, + "margin": -2, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "incident_inputs": [ + "timeline" + ], + "reusable_packaging": [ + "workflow", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + } + }, + { + "prompt": "Brainstorm how incident handling could improve next quarter without building the packet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "brainstorm" + ], + "positive_vote": 1, + "negative_vote": 3, + "margin": -2, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "baseline_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_packet_assembly": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_governed_workflow": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_one_off_update": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Turn these alert logs and responder notes into a governed incident packet with severity rationale and owner actions.", + "family": "blind_packet_assembly", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "strong_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Standardize this outage review, run severity assessment, and assemble incident communication into a reusable incident workflow.", + "family": "blind_governed_workflow", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "strong_positive_concepts": [ + "severity_assessment", + "comms_and_owners" + ], + "trigger_action_hits": [ + "run severity assessment", + "assemble incident communication" + ], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 6, + "negative_vote": 0, + "margin": 6, + "confidence": 1.0, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "incident communication" + ], + "governed_workflow": [ + "standardize" + ], + "reusable_packaging": [ + "reusable", + "workflow" + ], + "severity_assessment": [ + "severity assessment", + "severity", + "outage review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain how severity levels are decided during incidents.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "severity_assessment" + ], + "strong_positive_concepts": [ + "severity_assessment" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain" + ], + "positive_vote": 1, + "negative_vote": 1, + "margin": 0, + "confidence": 0.5, + "concept_evidence": { + "positive": { + "severity_assessment": [ + "severity" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Write a single incident update for customers from these notes.", + "family": "blind_one_off_update", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 0, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": {} + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Summarize the outage timeline, but do not package a governed workflow.", + "family": "blind_summary_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "incident_inputs", + "governed_workflow", + "reusable_packaging" + ], + "strong_positive_concepts": [ + "incident_inputs" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "summarize" + ], + "positive_vote": 3, + "negative_vote": 3, + "margin": 0, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "governed", + "governed workflow" + ], + "incident_inputs": [ + "timeline" + ], + "reusable_packaging": [ + "workflow", + "package" + ] + }, + "negative": { + "brainstorm_only": [ + "do not package" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Brainstorm how incident handling could improve next quarter without building the packet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "brainstorm" + ], + "positive_vote": 1, + "negative_vote": 3, + "margin": -2, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "winner_adversarial_holdout_report": { + "threshold": 0.34, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_governed_packet_from_noise": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_operational_skill_route": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_single_update_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_explain_and_summary_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_playbook_not_packet": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_debugging_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the incident chatter and assemble a governed incident packet from alerts, transcripts, and stakeholder requirements, including severity assessment and owner actions.", + "family": "adversarial_governed_packet_from_noise", + "score": 1.0, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 1.0, + "support_score": 0.2, + "lexical_support": 0.263, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_inputs": [ + "alerts", + "transcripts", + "stakeholder requirements" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity assessment", + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Treat this outage as a maintained operational skill: standardize the incident review, assess severity, and package communications.", + "family": "adversarial_operational_skill_route", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.789, + "support_score": 0.04, + "lexical_support": 0.235, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "standardize", + "maintained", + "operational skill" + ], + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "skill", + "package" + ], + "severity_assessment": [ + "severity", + "incident review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Write one short customer outage update from these incident notes and stop there.", + "family": "adversarial_single_update_collision", + "score": 0.056, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0.2, + "lexical_support": 0.077, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "incident notes" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain incident severity and summarize the outage timeline for training.", + "family": "adversarial_explain_and_summary_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.237, + "support_score": 0.2, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "timeline" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": { + "explain_only": [ + "explain incident severity" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future incident review playbook for later, but do not package a reusable workflow.", + "family": "adversarial_future_playbook_not_packet", + "score": 0.277, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.658, + "support_score": 0.04, + "lexical_support": 0.143, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "reusable", + "workflow", + "package" + ], + "severity_assessment": [ + "incident review" + ] + }, + "negative": { + "brainstorm_only": [ + "sketch a future", + "for later", + "do not package", + "do not package a reusable workflow" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Troubleshoot the outage and fix the service before we talk about reusable incident packets.", + "family": "adversarial_debugging_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "generic_debugging" + ], + "exclusive_negative_concepts": [ + "generic_debugging" + ], + "semantic_coverage": 0.0, + "support_score": 0.04, + "lexical_support": 0.154, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reusable_packaging": [ + "reusable" + ] + }, + "negative": { + "generic_debugging": [ + "fix the service" + ] + } + } + } + } + ] + } + }, + "current_adversarial_holdout_report": { + "threshold": 0.34, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_governed_packet_from_noise": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_operational_skill_route": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_single_update_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_explain_and_summary_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_playbook_not_packet": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_debugging_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the incident chatter and assemble a governed incident packet from alerts, transcripts, and stakeholder requirements, including severity assessment and owner actions.", + "family": "adversarial_governed_packet_from_noise", + "score": 1.0, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 1.0, + "support_score": 0.2, + "lexical_support": 0.263, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_inputs": [ + "alerts", + "transcripts", + "stakeholder requirements" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity assessment", + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Treat this outage as a maintained operational skill: standardize the incident review, assess severity, and package communications.", + "family": "adversarial_operational_skill_route", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.789, + "support_score": 0.04, + "lexical_support": 0.235, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "standardize", + "maintained", + "operational skill" + ], + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "skill", + "package" + ], + "severity_assessment": [ + "severity", + "incident review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Write one short customer outage update from these incident notes and stop there.", + "family": "adversarial_single_update_collision", + "score": 0.056, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0.2, + "lexical_support": 0.077, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "incident notes" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain incident severity and summarize the outage timeline for training.", + "family": "adversarial_explain_and_summary_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "severity_assessment" + ], + "extra_positive_concepts": [ + "incident_inputs" + ], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.237, + "support_score": 0.2, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "timeline" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": { + "explain_only": [ + "explain incident severity" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future incident review playbook for later, but do not package a reusable workflow.", + "family": "adversarial_future_playbook_not_packet", + "score": 0.277, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [ + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.658, + "support_score": 0.04, + "lexical_support": 0.143, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "reusable", + "workflow", + "package" + ], + "severity_assessment": [ + "incident review" + ] + }, + "negative": { + "brainstorm_only": [ + "sketch a future", + "for later", + "do not package", + "do not package a reusable workflow" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Troubleshoot the outage and fix the service before we talk about reusable incident packets.", + "family": "adversarial_debugging_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "severity_assessment", + "comms_and_owners", + "governed_workflow" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "reusable_packaging" + ], + "matched_negative_concepts": [ + "generic_debugging" + ], + "exclusive_negative_concepts": [ + "generic_debugging" + ], + "semantic_coverage": 0.0, + "support_score": 0.04, + "lexical_support": 0.154, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reusable_packaging": [ + "reusable" + ] + }, + "negative": { + "generic_debugging": [ + "fix the service" + ] + } + } + } + } + ] + } + }, + "baseline_adversarial_holdout_report": { + "threshold": 0.34, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_governed_packet_from_noise": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_operational_skill_route": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_single_update_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_explain_and_summary_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_playbook_not_packet": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_debugging_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the incident chatter and assemble a governed incident packet from alerts, transcripts, and stakeholder requirements, including severity assessment and owner actions.", + "family": "adversarial_governed_packet_from_noise", + "score": 0.99, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_inputs", + "incident_packet", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.96, + "support_score": 0, + "lexical_support": 0.579, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "comms_and_owners": [ + "owner", + "owner actions" + ], + "governed_workflow": [ + "governed" + ], + "incident_inputs": [ + "alerts", + "transcripts", + "stakeholder requirements" + ], + "incident_packet": [ + "governed incident packet" + ], + "severity_assessment": [ + "severity assessment", + "severity" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Treat this outage as a maintained operational skill: standardize the incident review, assess severity, and package communications.", + "family": "adversarial_operational_skill_route", + "score": 0.682, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "governed_workflow", + "incident_packet", + "reusable_packaging", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.64, + "support_score": 0, + "lexical_support": 0.412, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "governed_workflow": [ + "standardize", + "maintained", + "operational skill" + ], + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "skill", + "package" + ], + "severity_assessment": [ + "severity", + "incident review" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Write one short customer outage update from these incident notes and stop there.", + "family": "adversarial_single_update_collision", + "score": 0.215, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "incident_inputs" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.385, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "incident notes" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain incident severity and summarize the outage timeline for training.", + "family": "adversarial_explain_and_summary_collision", + "score": 0.012, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "incident_inputs", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0.26, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "incident_inputs": [ + "timeline" + ], + "severity_assessment": [ + "severity" + ] + }, + "negative": { + "explain_only": [ + "explain incident severity" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future incident review playbook for later, but do not package a reusable workflow.", + "family": "adversarial_future_playbook_not_packet", + "score": 0.201, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "incident_packet", + "reusable_packaging", + "severity_assessment" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.54, + "support_score": 0, + "lexical_support": 0.429, + "negative_penalty": 0.24, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "incident_packet": [ + "incident review" + ], + "reusable_packaging": [ + "reusable", + "workflow", + "package" + ], + "severity_assessment": [ + "incident review" + ] + }, + "negative": { + "brainstorm_only": [ + "sketch a future", + "for later", + "do not package", + "do not package a reusable workflow" + ] + } + } + } + }, + { + "prompt": "Troubleshoot the outage and fix the service before we talk about reusable incident packets.", + "family": "adversarial_debugging_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "comms_and_owners", + "governed_workflow", + "reusable_packaging" + ], + "matched_desired_concepts": [ + "reusable_packaging" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "generic_debugging" + ], + "exclusive_negative_concepts": [ + "generic_debugging" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.308, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reusable_packaging": [ + "reusable" + ] + }, + "negative": { + "generic_debugging": [ + "fix the service" + ] + } + } + } + } + ] + } + }, + "candidates": [ + { + "id": "current", + "label": "Current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "strategy": "current", + "estimated_tokens": 37, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 37 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "balanced", + "label": "Balanced", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "strategy": "balanced_template", + "estimated_tokens": 54, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 54 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "boundary", + "label": "Boundary", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "strategy": "boundary_template", + "estimated_tokens": 78, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 78 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "artifact_aware", + "label": "Artifact Aware", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Trigger when requests mention alerts, incident notes, transcripts, or stakeholder requirements and the job is to standardize incident review, run severity assessment, or assemble incident communication.", + "strategy": "artifact_template", + "estimated_tokens": 78, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 78 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "guardrail", + "label": "Guardrail", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "strategy": "guardrail_template", + "estimated_tokens": 51, + "dev": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.667, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 1, + -1.0, + -1.0, + -1.0, + -0.667, + 51 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + } + ], + "selection_logic": { + "priority": [ + "fewest false positives", + "fewest false negatives", + "highest near-neighbor pass rate", + "highest negative pass rate", + "highest precision", + "highest recall", + "shortest description" + ] + }, + "comparison": { + "winner_vs_current_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 1, + "improved_false_positives": 0, + "improved_false_negatives": 1, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 0.5, + "improved_recall": 0.5 + }, + "winner_vs_current_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 1, + "improved_false_positives": 0, + "improved_false_negatives": 1, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 0.5, + "improved_recall": 0.5 + }, + "winner_vs_baseline_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + } + }, + "acceptance_gates": { + "selection_basis": "dev only", + "holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.5, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.45, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.037, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.057, + "score_gap": 0.023, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.423, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "current_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.45, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.037, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.057, + "score_gap": 0.023, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.423, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "baseline_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.507, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.119, + "min_positive_score": 0.321, + "max_non_trigger_score": 0.22, + "score_gap": 0.101, + "threshold_margin": -0.159, + "positive_threshold_buffer": -0.159, + "negative_threshold_buffer": 0.26, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + } + }, + "blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.82, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.645, + "max_non_trigger_score": 0.228, + "score_gap": 0.417, + "threshold_margin": 0.165, + "positive_threshold_buffer": 0.165, + "negative_threshold_buffer": 0.252, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "current_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.82, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.645, + "max_non_trigger_score": 0.228, + "score_gap": 0.417, + "threshold_margin": 0.165, + "positive_threshold_buffer": 0.165, + "negative_threshold_buffer": 0.252, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "baseline_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.671, + "mean_non_trigger_score": 0.053, + "mean_near_neighbor_score": 0.003, + "min_positive_score": 0.55, + "max_non_trigger_score": 0.176, + "score_gap": 0.374, + "threshold_margin": 0.07, + "positive_threshold_buffer": 0.07, + "negative_threshold_buffer": 0.304, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "judge_blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "winner_calibration": null, + "current_calibration": null, + "baseline_calibration": null, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "adversarial_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.34, + "mean_positive_score": 0.907, + "mean_non_trigger_score": 0.083, + "mean_near_neighbor_score": 0.139, + "min_positive_score": 0.815, + "max_non_trigger_score": 0.277, + "score_gap": 0.538, + "threshold_margin": 0.063, + "positive_threshold_buffer": 0.475, + "negative_threshold_buffer": 0.063, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + }, + "current_calibration": { + "threshold": 0.34, + "mean_positive_score": 0.907, + "mean_non_trigger_score": 0.083, + "mean_near_neighbor_score": 0.139, + "min_positive_score": 0.815, + "max_non_trigger_score": 0.277, + "score_gap": 0.538, + "threshold_margin": 0.063, + "positive_threshold_buffer": 0.475, + "negative_threshold_buffer": 0.063, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + }, + "baseline_calibration": { + "threshold": 0.34, + "mean_positive_score": 0.836, + "mean_non_trigger_score": 0.107, + "mean_near_neighbor_score": 0.101, + "min_positive_score": 0.682, + "max_non_trigger_score": 0.215, + "score_gap": 0.467, + "threshold_margin": 0.125, + "positive_threshold_buffer": 0.342, + "negative_threshold_buffer": 0.125, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + } + }, + "summary": { + "winner_label": "Current", + "winner_tokens": 37, + "current_tokens": 37, + "winner_dev_total_errors": 0, + "current_dev_total_errors": 0, + "winner_holdout_total_errors": 1, + "current_holdout_total_errors": 1, + "winner_blind_holdout_total_errors": 0, + "current_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_total_errors": 0, + "current_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_total_errors": 0, + "current_adversarial_holdout_total_errors": 0, + "winner_judge_blind_agreement_rate": 1.0, + "winner_adversarial_risk_band": "watch", + "winner_adversarial_score_gap": 0.538, + "candidate_count": 5, + "baseline_tokens": 93, + "baseline_dev_total_errors": 0, + "baseline_holdout_total_errors": 1, + "baseline_blind_holdout_total_errors": 0, + "baseline_judge_blind_holdout_total_errors": 0, + "baseline_adversarial_holdout_total_errors": 0 + } +} diff --git a/examples/governed-incident-command/optimization/reports/description_optimization.md b/examples/governed-incident-command/optimization/reports/description_optimization.md new file mode 100644 index 0000000..874d859 --- /dev/null +++ b/examples/governed-incident-command/optimization/reports/description_optimization.md @@ -0,0 +1,64 @@ +# Governed Incident Description Optimization + +Winner: `Current` + +- current tokens: `37` +- winner tokens: `37` +- baseline tokens: `93` + +## Winner + +Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. + +## Candidate Ranking + +| Candidate | Tokens | Dev FP | Dev FN | Dev Near | Holdout FP | Holdout FN | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| `Current` | 37 | 0 | 0 | 1.0 | 0 | 1 | +| `Balanced` | 54 | 0 | 0 | 1.0 | 0 | 1 | +| `Boundary` | 78 | 0 | 0 | 1.0 | 0 | 1 | +| `Artifact Aware` | 78 | 0 | 0 | 1.0 | 0 | 1 | +| `Guardrail` | 51 | 0 | 1 | 1.0 | 0 | 1 | + +## Acceptance Gates + +| Gate | Winner FP | Winner FN | Current FP | Current FN | Baseline FP | Baseline FN | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| Holdout | 0 | 1 | 0 | 1 | 0 | 1 | +| Blind Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Judge Blind Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Adversarial Holdout | 0 | 0 | 0 | 0 | 0 | 0 | + +## Calibration + +| Gate | Winner Gap | Winner Risk | Winner Boundary Rate | Current Gap | Baseline Gap | +| --- | ---: | --- | ---: | ---: | ---: | +| Holdout | 0.023 | overlap | 0.0 | 0.023 | 0.101 | +| Blind Holdout | 0.417 | healthy | 0.0 | 0.417 | 0.374 | +| Adversarial Holdout | 0.538 | watch | 0.167 | 0.538 | 0.467 | + +## Judge Blind Summary + +| Gate | Winner Agreement | Winner Mean Confidence | Current Agreement | Baseline Agreement | +| --- | ---: | ---: | ---: | ---: | +| Judge Blind Holdout | 1.0 | 0.677 | 1.0 | 1.0 | + +## Family Health + +| Gate | Winner Clean Families | Winner Weakest Family | Current Clean Families | Baseline Clean Families | +| --- | --- | --- | --- | --- | +| Holdout | 5/6 | packet_assembly (1 errors) | 5/6 | 5/6 | +| Blind Holdout | 6/6 | blind_summary_only (0 errors) | 6/6 | 6/6 | +| Judge Blind Holdout | 6/6 | blind_summary_only (0 errors) | 6/6 | 6/6 | +| Adversarial Holdout | 6/6 | adversarial_single_update_collision (0 errors) | 6/6 | 6/6 | + +## Selection Logic + +Ordered by: +- fewest false positives +- fewest false negatives +- highest near-neighbor pass rate +- highest negative pass rate +- highest precision +- highest recall +- shortest description diff --git a/examples/governed-incident-command/optimization/semantic_config.json b/examples/governed-incident-command/optimization/semantic_config.json new file mode 100644 index 0000000..20cdcf2 --- /dev/null +++ b/examples/governed-incident-command/optimization/semantic_config.json @@ -0,0 +1,160 @@ +{ + "optimizer_hints": { + "capability": "Build governed incident command packets", + "inputs": [ + "alerts", + "incident notes", + "transcripts", + "stakeholder requirements" + ], + "trigger_actions": [ + "standardize incident review", + "run severity assessment", + "assemble incident communication", + "package a reusable incident workflow" + ], + "exclusions": [ + "generic debugging", + "one-off incident explanations", + "drafting a single update", + "brainstorming response ideas" + ], + "artifacts": [ + "alerts", + "incident notes", + "transcripts", + "stakeholder requirements" + ] + }, + "fallback_positive_concepts": [ + "incident_packet", + "incident_inputs", + "severity_assessment", + "governed_workflow" + ], + "positive_concepts": { + "incident_packet": { + "weight": 0.32, + "phrases": [ + "incident command packet", + "incident command packets", + "governed incident packet", + "incident packet assembly", + "incident review skill", + "incident review", + "standardize incident review" + ] + }, + "incident_inputs": { + "weight": 0.2, + "phrases": [ + "incident notes", + "alerts", + "transcripts", + "timeline", + "stakeholder requirements", + "outage workflow" + ] + }, + "severity_assessment": { + "weight": 0.18, + "phrases": [ + "severity assessment", + "severity", + "impact scope", + "incident review", + "outage review", + "sev" + ] + }, + "comms_and_owners": { + "weight": 0.16, + "phrases": [ + "stakeholder update", + "incident communication", + "owner", + "owners", + "owner actions", + "action ownership", + "next actions" + ] + }, + "governed_workflow": { + "weight": 0.1, + "phrases": [ + "governed", + "governed workflow", + "reusable governed workflow", + "standardize", + "reviewed", + "audited", + "maintained", + "operational skill" + ] + }, + "reusable_packaging": { + "weight": 0.04, + "phrases": [ + "reusable", + "workflow", + "skill", + "package" + ] + } + }, + "negative_concepts": { + "generic_debugging": { + "weight": 0.3, + "exclusive": true, + "phrases": [ + "debug this outage", + "fix the service", + "generic debugging", + "troubleshoot the incident" + ] + }, + "explain_only": { + "weight": 0.26, + "exclusive": true, + "phrases": [ + "explain incident severity", + "what does severity mean", + "explain the concept", + "teach me incident response" + ] + }, + "one_off_update": { + "weight": 0.34, + "exclusive": true, + "phrases": [ + "draft one short outage update", + "single status update", + "one-off update", + "just write the customer update" + ] + }, + "brainstorm_only": { + "weight": 0.24, + "exclusive": true, + "phrases": [ + "brainstorm", + "sketch a future", + "for later", + "possible response ideas", + "improve incident handling", + "ideas for later", + "do not package", + "do not package a reusable workflow" + ] + }, + "summary_only": { + "weight": 0.24, + "exclusive": true, + "phrases": [ + "summary only", + "summarize the incident", + "recap the outage" + ] + } + } +} diff --git a/examples/governed-incident-command/raw-workflow.md b/examples/governed-incident-command/raw-workflow.md new file mode 100644 index 0000000..927183a --- /dev/null +++ b/examples/governed-incident-command/raw-workflow.md @@ -0,0 +1,18 @@ +# Raw Workflow + +We handle production incidents across support, product, and infrastructure. The current process is scattered: + +- incident notes live in chat +- severity decisions live in a spreadsheet +- stakeholder updates are copied from older docs +- the post-incident packet is assembled manually + +The team wants one reusable skill that can: + +- turn alerts, notes, and transcripts into a structured incident command packet +- decide severity using our matrix +- prepare internal and external stakeholder updates +- stop when evidence is missing instead of fabricating a clean story +- stay governed because this workflow is reused in every serious incident review + +This is not a one-off incident summary. It is a recurring operational workflow that must be reviewed monthly and audited over time. diff --git a/examples/simple-note-cleanup/design-summary.md b/examples/simple-note-cleanup/design-summary.md new file mode 100644 index 0000000..d48d5b2 --- /dev/null +++ b/examples/simple-note-cleanup/design-summary.md @@ -0,0 +1,7 @@ +# Design Summary + +- Skill tier: simple +- Trigger surface: meeting notes cleanup, action item extraction, decision summary +- Deterministic content: output sections +- Flexible content: wording and compression style +- Package choice: `SKILL.md` + `agents/interface.yaml` diff --git a/examples/simple-note-cleanup/generated-skill/SKILL.md b/examples/simple-note-cleanup/generated-skill/SKILL.md new file mode 100644 index 0000000..956b302 --- /dev/null +++ b/examples/simple-note-cleanup/generated-skill/SKILL.md @@ -0,0 +1,13 @@ +--- +name: note-cleanup +description: Clean messy meeting notes into structured markdown summaries. Use when asked to organize meeting notes, extract action items, separate decisions from open questions, or turn rough notes into a clean recap. +--- + +# Note Cleanup + +## Workflow + +1. Read the notes fully before restructuring. +2. Remove duplicates and obvious noise. +3. Produce sections for summary, decisions, action items, and open questions. +4. Preserve important names, dates, and owners. diff --git a/examples/simple-note-cleanup/generated-skill/agents/interface.yaml b/examples/simple-note-cleanup/generated-skill/agents/interface.yaml new file mode 100644 index 0000000..d810b95 --- /dev/null +++ b/examples/simple-note-cleanup/generated-skill/agents/interface.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Note Cleanup" + short_description: "Turn rough meeting notes into clean recaps" + default_prompt: "Use $note-cleanup to clean these meeting notes into a summary with decisions and action items." diff --git a/examples/simple-note-cleanup/raw-workflow.md b/examples/simple-note-cleanup/raw-workflow.md new file mode 100644 index 0000000..dd599bf --- /dev/null +++ b/examples/simple-note-cleanup/raw-workflow.md @@ -0,0 +1,14 @@ +# Raw Workflow + +After meetings, I usually paste messy notes into chat and ask for cleanup. + +What I actually need: + +- remove duplicates +- group action items +- separate decisions from open questions +- output a clean markdown summary + +Typical prompt: + +"Turn these rough meeting notes into a clean summary with action items and decisions." diff --git a/examples/team-frontend-review/design-summary.md b/examples/team-frontend-review/design-summary.md new file mode 100644 index 0000000..09b2094 --- /dev/null +++ b/examples/team-frontend-review/design-summary.md @@ -0,0 +1,7 @@ +# Design Summary + +- Skill tier: team-level production +- Trigger surface: React review, frontend review, accessibility review, UI security review +- Deterministic content: review checklist and output format +- Flexible content: severity judgment and explanation +- Package choice: `SKILL.md`, `agents/interface.yaml`, `references/checklist.md`, `evals/` diff --git a/examples/team-frontend-review/generated-skill/SKILL.md b/examples/team-frontend-review/generated-skill/SKILL.md new file mode 100644 index 0000000..2a63353 --- /dev/null +++ b/examples/team-frontend-review/generated-skill/SKILL.md @@ -0,0 +1,26 @@ +--- +name: frontend-review +description: Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. +--- + +# Frontend Review + +Review UI code with a pre-merge mindset. Findings come first. Focus on behavior regressions, accessibility gaps, risky form handling, unsafe rendering, and missing user states. + +## Workflow + +1. Identify the UI surface and user flows affected. +2. Check accessibility, security-sensitive inputs, loading states, and error states. +3. Report findings by severity with concrete code references. +4. Prefer actionable fixes over generic advice. + +## Output Contract + +- Start with findings, ordered by severity. +- Include file references for each finding. +- Call out open questions separately from confirmed issues. +- If there are no findings, say that explicitly and note residual risks or test gaps. + +## Reference Map + +- Read `references/checklist.md` for the review standard. diff --git a/examples/team-frontend-review/generated-skill/agents/interface.yaml b/examples/team-frontend-review/generated-skill/agents/interface.yaml new file mode 100644 index 0000000..1b911d6 --- /dev/null +++ b/examples/team-frontend-review/generated-skill/agents/interface.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Frontend Review" + short_description: "Review UI code for a11y, security, and UX regressions" + default_prompt: "Use $frontend-review to review this React code before merge." diff --git a/examples/team-frontend-review/generated-skill/references/checklist.md b/examples/team-frontend-review/generated-skill/references/checklist.md new file mode 100644 index 0000000..dc27dec --- /dev/null +++ b/examples/team-frontend-review/generated-skill/references/checklist.md @@ -0,0 +1,13 @@ +# Review Checklist + +- Accessibility semantics +- Keyboard navigation +- Form validation and unsafe rendering risks +- Loading states +- Error states +- Empty states +- Focus handling after dialog open/close +- Color contrast and non-text cues +- Dangerous HTML injection paths +- Missing disabled and pending states for forms +- Inconsistent error recovery or retry flow diff --git a/examples/team-frontend-review/optimization/adversarial/trigger_cases.json b/examples/team-frontend-review/optimization/adversarial/trigger_cases.json new file mode 100644 index 0000000..2cb4e63 --- /dev/null +++ b/examples/team-frontend-review/optimization/adversarial/trigger_cases.json @@ -0,0 +1,33 @@ +{ + "recommended_threshold": 0.34, + "should_trigger": [ + { + "family": "adversarial_noisy_premerge_review", + "text": "Ignore the feature-request chatter and run a pre-merge frontend review on this React diff for keyboard navigation, unsafe rendering, and missing empty states." + }, + { + "family": "adversarial_quality_gate_review", + "text": "Treat this as a quality gate: review the component for a11y, form handling, and behavior regressions instead of rewriting it." + } + ], + "should_not_trigger": [ + { + "family": "adversarial_build_with_a11y_words", + "text": "Build a React form with accessible labels, safe rendering, and polished empty states." + }, + { + "family": "adversarial_explain_with_review_terms", + "text": "Explain keyboard navigation problems in this component and teach me the basics." + } + ], + "near_neighbor": [ + { + "family": "adversarial_checklist_not_review", + "text": "Brainstorm a reusable frontend review checklist for later, but do not inspect the code or run a review." + }, + { + "family": "adversarial_fix_only_collision", + "text": "Debug this component's form handling bug and fix the unsafe rendering issue." + } + ] +} diff --git a/examples/team-frontend-review/optimization/baseline_description.txt b/examples/team-frontend-review/optimization/baseline_description.txt new file mode 100644 index 0000000..53dc71a --- /dev/null +++ b/examples/team-frontend-review/optimization/baseline_description.txt @@ -0,0 +1 @@ +Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality. diff --git a/examples/team-frontend-review/optimization/blind_holdout/trigger_cases.json b/examples/team-frontend-review/optimization/blind_holdout/trigger_cases.json new file mode 100644 index 0000000..31fc0e1 --- /dev/null +++ b/examples/team-frontend-review/optimization/blind_holdout/trigger_cases.json @@ -0,0 +1,33 @@ +{ + "recommended_threshold": 0.48, + "should_trigger": [ + { + "text": "Review this React pull request for a11y regressions, unsafe rendering, and missing empty states.", + "family": "blind_pr_review" + }, + { + "text": "Run a pre-merge frontend review on this diff for form safety, accessibility gaps, and missing states.", + "family": "blind_premerge" + } + ], + "should_not_trigger": [ + { + "text": "Build a new React settings page and add error handling.", + "family": "blind_build_only" + }, + { + "text": "Explain why keyboard accessibility matters in forms.", + "family": "blind_explain_only" + } + ], + "near_neighbor": [ + { + "text": "Draft a reusable frontend review checklist, but do not inspect the code.", + "family": "blind_checklist_only" + }, + { + "text": "Fix the broken dropdown behavior in this component.", + "family": "blind_fix_only" + } + ] +} diff --git a/examples/team-frontend-review/optimization/dev/trigger_cases.json b/examples/team-frontend-review/optimization/dev/trigger_cases.json new file mode 100644 index 0000000..77fa609 --- /dev/null +++ b/examples/team-frontend-review/optimization/dev/trigger_cases.json @@ -0,0 +1,53 @@ +{ + "recommended_threshold": 0.48, + "should_trigger": [ + { + "text": "Review this React checkout form for accessibility and risky frontend issues before merge.", + "family": "premerge_review" + }, + { + "text": "Do a frontend review on these UI components and check loading, error, and empty states.", + "family": "state_review" + }, + { + "text": "Audit this signup form for a11y, unsafe rendering, and UX regressions.", + "family": "security_and_a11y" + }, + { + "text": "Review this React diff with a pre-merge mindset and call out UI quality risks.", + "family": "premerge_review" + } + ], + "should_not_trigger": [ + { + "text": "Build a React login page with loading and error states.", + "family": "build_only" + }, + { + "text": "Explain how accessibility works in React forms.", + "family": "explain_only" + }, + { + "text": "Review this backend endpoint and database migration.", + "family": "backend_only" + }, + { + "text": "Translate these frontend review comments into Japanese.", + "family": "translate_only" + } + ], + "near_neighbor": [ + { + "text": "Brainstorm ways to improve this dashboard UX, but do not review code.", + "family": "brainstorm_only" + }, + { + "text": "Write a frontend review checklist without reviewing the code itself.", + "family": "checklist_only" + }, + { + "text": "Fix the failing validation bug in this form component.", + "family": "fix_only" + } + ] +} diff --git a/examples/team-frontend-review/optimization/holdout/trigger_cases.json b/examples/team-frontend-review/optimization/holdout/trigger_cases.json new file mode 100644 index 0000000..178ccc8 --- /dev/null +++ b/examples/team-frontend-review/optimization/holdout/trigger_cases.json @@ -0,0 +1,33 @@ +{ + "recommended_threshold": 0.48, + "should_trigger": [ + { + "text": "Give this React modal a pre-merge frontend review for a11y, risky rendering, and missing states.", + "family": "premerge_review" + }, + { + "text": "Review this UI diff for accessibility gaps, unsafe form handling, and UX regressions.", + "family": "security_and_a11y" + } + ], + "should_not_trigger": [ + { + "text": "Create a polished React dashboard page from scratch.", + "family": "build_only" + }, + { + "text": "Explain keyboard accessibility rules for beginners.", + "family": "explain_only" + } + ], + "near_neighbor": [ + { + "text": "Discuss possible UI improvements for this checkout flow, but do not inspect the code yet.", + "family": "brainstorm_only" + }, + { + "text": "Repair this broken input validation behavior in the form component.", + "family": "fix_only" + } + ] +} diff --git a/examples/team-frontend-review/optimization/reports/description_optimization.json b/examples/team-frontend-review/optimization/reports/description_optimization.json new file mode 100644 index 0000000..1d92787 --- /dev/null +++ b/examples/team-frontend-review/optimization/reports/description_optimization.json @@ -0,0 +1,6798 @@ +{ + "current_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "current_candidate": { + "id": "current", + "label": "Current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "strategy": "current", + "estimated_tokens": 50, + "dev": { + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 3, + -1.0, + -1.0, + -1.0, + -0.25, + 50 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "baseline": { + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "estimated_tokens": 52, + "dev": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "bucket_stats": { + "should_trigger": { + "total": 4, + "passed": 1, + "pass_rate": 0.25 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + } + }, + "family_stats": { + "premerge_review": { + "total": 2, + "passed": 0, + "false_positives": 0, + "false_negatives": 2, + "pass_rate": 0.0 + }, + "state_review": { + "total": 1, + "passed": 0, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.0 + }, + "security_and_a11y": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "backend_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "premerge_review", + "prompt": "Review this React checkout form for accessibility and risky frontend issues before merge.", + "score": 0.316, + "reason": "false_negative", + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "matched_negative_concepts": [] + }, + { + "bucket": "should_trigger", + "family": "state_review", + "prompt": "Do a frontend review on these UI components and check loading, error, and empty states.", + "score": 0.424, + "reason": "false_negative", + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "matched_negative_concepts": [] + }, + { + "bucket": "should_trigger", + "family": "premerge_review", + "prompt": "Review this React diff with a pre-merge mindset and call out UI quality risks.", + "score": 0.188, + "reason": "false_negative", + "matched_desired_concepts": [ + "frontend_scope", + "ux_regression" + ], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Review this React checkout form for accessibility and risky frontend issues before merge.", + "family": "premerge_review", + "score": 0.316, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.692, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "react", + "frontend", + "form" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Do a frontend review on these UI components and check loading, error, and empty states.", + "family": "state_review", + "score": 0.424, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.429, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend", + "ui" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Audit this signup form for a11y, unsafe rendering, and UX regressions.", + "family": "security_and_a11y", + "score": 0.483, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.455, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "form" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Review this React diff with a pre-merge mindset and call out UI quality risks.", + "family": "premerge_review", + "score": 0.188, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.12, + "support_score": 0, + "lexical_support": 0.467, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react", + "ui" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a React login page with loading and error states.", + "family": "build_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "build_ui" + ], + "exclusive_negative_concepts": [ + "build_ui" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": { + "build_ui": [ + "build a react" + ] + } + } + } + }, + { + "prompt": "Explain how accessibility works in React forms.", + "family": "explain_only", + "score": 0.295, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.429, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Review this backend endpoint and database migration.", + "family": "backend_only", + "score": 0.023, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + }, + { + "prompt": "Translate these frontend review comments into Japanese.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.35, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": { + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Brainstorm ways to improve this dashboard UX, but do not review code.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + }, + { + "prompt": "Write a frontend review checklist without reviewing the code itself.", + "family": "checklist_only", + "score": 0.414, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Fix the failing validation bug in this form component.", + "family": "fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "holdout": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "premerge_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "security_and_a11y": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Give this React modal a pre-merge frontend review for a11y, risky rendering, and missing states.", + "family": "premerge_review", + "score": 0.869, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "state_coverage", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.82, + "support_score": 0, + "lexical_support": 0.688, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react", + "frontend" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Review this UI diff for accessibility gaps, unsafe form handling, and UX regressions.", + "family": "security_and_a11y", + "score": 0.489, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.538, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "ui", + "form" + ], + "ui_security": [ + "form handling" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Create a polished React dashboard page from scratch.", + "family": "build_only", + "score": 0.047, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain keyboard accessibility rules for beginners.", + "family": "explain_only", + "score": 0.211, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Discuss possible UI improvements for this checkout flow, but do not inspect the code yet.", + "family": "brainstorm_only", + "score": 0.053, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "ui" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Repair this broken input validation behavior in the form component.", + "family": "fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ] + }, + "negative": {} + } + } + } + ] + } + } + }, + "winner": { + "id": "current", + "label": "Current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "strategy": "current", + "estimated_tokens": 50, + "dev": { + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 3, + -1.0, + -1.0, + -1.0, + -0.25, + 50 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "winner_dev_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "bucket_stats": { + "should_trigger": { + "total": 4, + "passed": 1, + "pass_rate": 0.25 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + } + }, + "family_stats": { + "premerge_review": { + "total": 2, + "passed": 0, + "false_positives": 0, + "false_negatives": 2, + "pass_rate": 0.0 + }, + "state_review": { + "total": 1, + "passed": 0, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.0 + }, + "security_and_a11y": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "backend_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "premerge_review", + "prompt": "Review this React checkout form for accessibility and risky frontend issues before merge.", + "score": 0.304, + "reason": "false_negative", + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "matched_negative_concepts": [] + }, + { + "bucket": "should_trigger", + "family": "state_review", + "prompt": "Do a frontend review on these UI components and check loading, error, and empty states.", + "score": 0.435, + "reason": "false_negative", + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "matched_negative_concepts": [] + }, + { + "bucket": "should_trigger", + "family": "premerge_review", + "prompt": "Review this React diff with a pre-merge mindset and call out UI quality risks.", + "score": 0.188, + "reason": "false_negative", + "matched_desired_concepts": [ + "frontend_scope", + "ux_regression" + ], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Review this React checkout form for accessibility and risky frontend issues before merge.", + "family": "premerge_review", + "score": 0.304, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.538, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "react", + "frontend", + "form" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Do a frontend review on these UI components and check loading, error, and empty states.", + "family": "state_review", + "score": 0.435, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.571, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend", + "ui" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Audit this signup form for a11y, unsafe rendering, and UX regressions.", + "family": "security_and_a11y", + "score": 0.497, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.636, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "form" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Review this React diff with a pre-merge mindset and call out UI quality risks.", + "family": "premerge_review", + "score": 0.188, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.12, + "support_score": 0, + "lexical_support": 0.467, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react", + "ui" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a React login page with loading and error states.", + "family": "build_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "build_ui" + ], + "exclusive_negative_concepts": [ + "build_ui" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": { + "build_ui": [ + "build a react" + ] + } + } + } + }, + { + "prompt": "Explain how accessibility works in React forms.", + "family": "explain_only", + "score": 0.284, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Review this backend endpoint and database migration.", + "family": "backend_only", + "score": 0.023, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + }, + { + "prompt": "Translate these frontend review comments into Japanese.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.35, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": { + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Brainstorm ways to improve this dashboard UX, but do not review code.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + }, + { + "prompt": "Write a frontend review checklist without reviewing the code itself.", + "family": "checklist_only", + "score": 0.422, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Fix the failing validation bug in this form component.", + "family": "fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "winner_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "premerge_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "security_and_a11y": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Give this React modal a pre-merge frontend review for a11y, risky rendering, and missing states.", + "family": "premerge_review", + "score": 0.874, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "state_coverage", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.82, + "support_score": 0, + "lexical_support": 0.75, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react", + "frontend" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Review this UI diff for accessibility gaps, unsafe form handling, and UX regressions.", + "family": "security_and_a11y", + "score": 0.496, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.615, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "ui", + "form" + ], + "ui_security": [ + "form handling" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Create a polished React dashboard page from scratch.", + "family": "build_only", + "score": 0.057, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain keyboard accessibility rules for beginners.", + "family": "explain_only", + "score": 0.211, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Discuss possible UI improvements for this checkout flow, but do not inspect the code yet.", + "family": "brainstorm_only", + "score": 0.053, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "ui" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Repair this broken input validation behavior in the form component.", + "family": "fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "current_dev_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "bucket_stats": { + "should_trigger": { + "total": 4, + "passed": 1, + "pass_rate": 0.25 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 3, + "passed": 3, + "pass_rate": 1.0 + } + }, + "family_stats": { + "premerge_review": { + "total": 2, + "passed": 0, + "false_positives": 0, + "false_negatives": 2, + "pass_rate": 0.0 + }, + "state_review": { + "total": 1, + "passed": 0, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.0 + }, + "security_and_a11y": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "backend_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "premerge_review", + "prompt": "Review this React checkout form for accessibility and risky frontend issues before merge.", + "score": 0.304, + "reason": "false_negative", + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "matched_negative_concepts": [] + }, + { + "bucket": "should_trigger", + "family": "state_review", + "prompt": "Do a frontend review on these UI components and check loading, error, and empty states.", + "score": 0.435, + "reason": "false_negative", + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "matched_negative_concepts": [] + }, + { + "bucket": "should_trigger", + "family": "premerge_review", + "prompt": "Review this React diff with a pre-merge mindset and call out UI quality risks.", + "score": 0.188, + "reason": "false_negative", + "matched_desired_concepts": [ + "frontend_scope", + "ux_regression" + ], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Review this React checkout form for accessibility and risky frontend issues before merge.", + "family": "premerge_review", + "score": 0.304, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.538, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "react", + "frontend", + "form" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Do a frontend review on these UI components and check loading, error, and empty states.", + "family": "state_review", + "score": 0.435, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.571, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend", + "ui" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Audit this signup form for a11y, unsafe rendering, and UX regressions.", + "family": "security_and_a11y", + "score": 0.497, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.636, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "form" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Review this React diff with a pre-merge mindset and call out UI quality risks.", + "family": "premerge_review", + "score": 0.188, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.12, + "support_score": 0, + "lexical_support": 0.467, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react", + "ui" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a React login page with loading and error states.", + "family": "build_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "build_ui" + ], + "exclusive_negative_concepts": [ + "build_ui" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": { + "build_ui": [ + "build a react" + ] + } + } + } + }, + { + "prompt": "Explain how accessibility works in React forms.", + "family": "explain_only", + "score": 0.284, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Review this backend endpoint and database migration.", + "family": "backend_only", + "score": 0.023, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": {} + } + } + }, + { + "prompt": "Translate these frontend review comments into Japanese.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.35, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": { + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Brainstorm ways to improve this dashboard UX, but do not review code.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + }, + { + "prompt": "Write a frontend review checklist without reviewing the code itself.", + "family": "checklist_only", + "score": 0.422, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.4, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Fix the failing validation bug in this form component.", + "family": "fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "current_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "premerge_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "security_and_a11y": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Give this React modal a pre-merge frontend review for a11y, risky rendering, and missing states.", + "family": "premerge_review", + "score": 0.874, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "state_coverage", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.82, + "support_score": 0, + "lexical_support": 0.75, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react", + "frontend" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Review this UI diff for accessibility gaps, unsafe form handling, and UX regressions.", + "family": "security_and_a11y", + "score": 0.496, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.615, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "ui", + "form" + ], + "ui_security": [ + "form handling" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Create a polished React dashboard page from scratch.", + "family": "build_only", + "score": 0.057, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain keyboard accessibility rules for beginners.", + "family": "explain_only", + "score": 0.211, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Discuss possible UI improvements for this checkout flow, but do not inspect the code yet.", + "family": "brainstorm_only", + "score": 0.053, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "ui" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Repair this broken input validation behavior in the form component.", + "family": "fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "winner_blind_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_pr_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_premerge": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Review this React pull request for a11y regressions, unsafe rendering, and missing empty states.", + "family": "blind_pr_review", + "score": 0.504, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.714, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Run a pre-merge frontend review on this diff for form safety, accessibility gaps, and missing states.", + "family": "blind_premerge", + "score": 0.866, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "state_coverage", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.82, + "support_score": 0, + "lexical_support": 0.647, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "frontend", + "form" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a new React settings page and add error handling.", + "family": "blind_build_only", + "score": 0.061, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain why keyboard accessibility matters in forms.", + "family": "blind_explain_only", + "score": 0.195, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a reusable frontend review checklist, but do not inspect the code.", + "family": "blind_checklist_only", + "score": 0.416, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Fix the broken dropdown behavior in this component.", + "family": "blind_fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "current_blind_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_pr_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_premerge": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Review this React pull request for a11y regressions, unsafe rendering, and missing empty states.", + "family": "blind_pr_review", + "score": 0.504, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.714, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Run a pre-merge frontend review on this diff for form safety, accessibility gaps, and missing states.", + "family": "blind_premerge", + "score": 0.866, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "state_coverage", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.82, + "support_score": 0, + "lexical_support": 0.647, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "frontend", + "form" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a new React settings page and add error handling.", + "family": "blind_build_only", + "score": 0.061, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain why keyboard accessibility matters in forms.", + "family": "blind_explain_only", + "score": 0.195, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a reusable frontend review checklist, but do not inspect the code.", + "family": "blind_checklist_only", + "score": 0.416, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.333, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Fix the broken dropdown behavior in this component.", + "family": "blind_fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "baseline_blind_holdout_report": { + "threshold": 0.48, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_pr_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_premerge": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Review this React pull request for a11y regressions, unsafe rendering, and missing empty states.", + "family": "blind_pr_review", + "score": 0.492, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.42, + "support_score": 0, + "lexical_support": 0.571, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Run a pre-merge frontend review on this diff for form safety, accessibility gaps, and missing states.", + "family": "blind_premerge", + "score": 0.857, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "state_coverage", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.82, + "support_score": 0, + "lexical_support": 0.529, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "frontend", + "form" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a new React settings page and add error handling.", + "family": "blind_build_only", + "score": 0.053, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain why keyboard accessibility matters in forms.", + "family": "blind_explain_only", + "score": 0.207, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.2, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a reusable frontend review checklist, but do not inspect the code.", + "family": "blind_checklist_only", + "score": 0.41, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Fix the broken dropdown behavior in this component.", + "family": "blind_fix_only", + "score": 0.037, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component" + ] + }, + "negative": {} + } + } + } + ] + } + }, + "winner_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_pr_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_premerge": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Review this React pull request for a11y regressions, unsafe rendering, and missing empty states.", + "family": "blind_pr_review", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "accessibility", + "ui_security", + "frontend_scope" + ], + "strong_positive_concepts": [ + "accessibility", + "ui_security" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 6, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Run a pre-merge frontend review on this diff for form safety, accessibility gaps, and missing states.", + "family": "blind_premerge", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "review_frontend", + "accessibility", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "strong_positive_concepts": [ + "review_frontend", + "accessibility", + "state_coverage" + ], + "trigger_action_hits": [ + "run a pre-merge frontend review" + ], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 7, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 6, + "negative_vote": 0, + "margin": 6, + "confidence": 1.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "frontend", + "form" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a new React settings page and add error handling.", + "family": "blind_build_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "frontend_scope" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 0, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Explain why keyboard accessibility matters in forms.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "accessibility" + ], + "strong_positive_concepts": [ + "accessibility" + ], + "trigger_action_hits": [], + "input_hits": [ + "forms" + ], + "artifact_hits": [ + "forms" + ], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain" + ], + "positive_vote": 3, + "negative_vote": 1, + "margin": 2, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a reusable frontend review checklist, but do not inspect the code.", + "family": "blind_checklist_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "review_frontend", + "frontend_scope" + ], + "strong_positive_concepts": [ + "review_frontend" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 0, + "margin": 2, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Fix the broken dropdown behavior in this component.", + "family": "blind_fix_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "frontend_scope" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 0, + "negative_vote": 0, + "margin": 0, + "confidence": 0.5, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "current_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_pr_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_premerge": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Review this React pull request for a11y regressions, unsafe rendering, and missing empty states.", + "family": "blind_pr_review", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "accessibility", + "ui_security", + "frontend_scope" + ], + "strong_positive_concepts": [ + "accessibility", + "ui_security" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 6, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Run a pre-merge frontend review on this diff for form safety, accessibility gaps, and missing states.", + "family": "blind_premerge", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "review_frontend", + "accessibility", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "strong_positive_concepts": [ + "review_frontend", + "accessibility", + "state_coverage" + ], + "trigger_action_hits": [ + "run a pre-merge frontend review" + ], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 7, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 6, + "negative_vote": 0, + "margin": 6, + "confidence": 1.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "frontend", + "form" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a new React settings page and add error handling.", + "family": "blind_build_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "frontend_scope" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 0, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Explain why keyboard accessibility matters in forms.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "accessibility" + ], + "strong_positive_concepts": [ + "accessibility" + ], + "trigger_action_hits": [], + "input_hits": [ + "forms" + ], + "artifact_hits": [ + "forms" + ], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain" + ], + "positive_vote": 3, + "negative_vote": 1, + "margin": 2, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a reusable frontend review checklist, but do not inspect the code.", + "family": "blind_checklist_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "review_frontend", + "frontend_scope" + ], + "strong_positive_concepts": [ + "review_frontend" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 0, + "margin": 2, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Fix the broken dropdown behavior in this component.", + "family": "blind_fix_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "frontend_scope" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 0, + "negative_vote": 0, + "margin": 0, + "confidence": 0.5, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "baseline_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_pr_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_premerge": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_build_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_checklist_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_fix_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Review this React pull request for a11y regressions, unsafe rendering, and missing empty states.", + "family": "blind_pr_review", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "accessibility", + "ui_security", + "frontend_scope" + ], + "strong_positive_concepts": [ + "accessibility", + "ui_security" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 6, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "react" + ], + "ui_security": [ + "unsafe rendering" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Run a pre-merge frontend review on this diff for form safety, accessibility gaps, and missing states.", + "family": "blind_premerge", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "review_frontend", + "accessibility", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "strong_positive_concepts": [ + "review_frontend", + "accessibility", + "state_coverage" + ], + "trigger_action_hits": [ + "run a pre-merge frontend review" + ], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 7, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 6, + "negative_vote": 0, + "margin": 6, + "confidence": 1.0, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ], + "frontend_scope": [ + "frontend", + "form" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "state_coverage": [ + "missing states" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a new React settings page and add error handling.", + "family": "blind_build_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "frontend_scope" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 0, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Explain why keyboard accessibility matters in forms.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "accessibility" + ], + "strong_positive_concepts": [ + "accessibility" + ], + "trigger_action_hits": [], + "input_hits": [ + "forms" + ], + "artifact_hits": [ + "forms" + ], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain" + ], + "positive_vote": 3, + "negative_vote": 1, + "margin": 2, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "accessibility": [ + "accessibility" + ] + }, + "negative": {} + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Draft a reusable frontend review checklist, but do not inspect the code.", + "family": "blind_checklist_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "review_frontend", + "frontend_scope" + ], + "strong_positive_concepts": [ + "review_frontend" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 0, + "margin": 2, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Fix the broken dropdown behavior in this component.", + "family": "blind_fix_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "frontend_scope" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 0, + "negative_vote": 0, + "margin": 0, + "confidence": 0.5, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "winner_adversarial_holdout_report": { + "threshold": 0.34, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_premerge_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_quality_gate_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_build_with_a11y_words": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_explain_with_review_terms": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_checklist_not_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_fix_only_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the feature-request chatter and run a pre-merge frontend review on this React diff for keyboard navigation, unsafe rendering, and missing empty states.", + "family": "adversarial_noisy_premerge_review", + "score": 0.876, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "ui_security", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.84, + "support_score": 0, + "lexical_support": 0.542, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "keyboard navigation" + ], + "frontend_scope": [ + "react", + "frontend" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "ui_security": [ + "unsafe rendering" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Treat this as a quality gate: review the component for a11y, form handling, and behavior regressions instead of rewriting it.", + "family": "adversarial_quality_gate_review", + "score": 0.544, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.5, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "component", + "form" + ], + "ui_security": [ + "form handling" + ], + "ux_regression": [ + "quality gate" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a React form with accessible labels, safe rendering, and polished empty states.", + "family": "adversarial_build_with_a11y_words", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "build_ui" + ], + "exclusive_negative_concepts": [ + "build_ui" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.385, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react", + "form" + ] + }, + "negative": { + "build_ui": [ + "build a react" + ] + } + } + } + }, + { + "prompt": "Explain keyboard navigation problems in this component and teach me the basics.", + "family": "adversarial_explain_with_review_terms", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "keyboard navigation" + ], + "frontend_scope": [ + "component" + ] + }, + "negative": { + "explain_only": [ + "teach me" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Brainstorm a reusable frontend review checklist for later, but do not inspect the code or run a review.", + "family": "adversarial_checklist_not_review", + "score": 0.035, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.438, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + }, + { + "prompt": "Debug this component's form handling bug and fix the unsafe rendering issue.", + "family": "adversarial_fix_only_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "fix_only" + ], + "exclusive_negative_concepts": [ + "fix_only" + ], + "semantic_coverage": 0.22, + "support_score": 0, + "lexical_support": 0.231, + "negative_penalty": 0.18, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ], + "ui_security": [ + "unsafe rendering", + "form handling" + ] + }, + "negative": { + "fix_only": [ + "debug this component" + ] + } + } + } + } + ] + } + }, + "current_adversarial_holdout_report": { + "threshold": 0.34, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_premerge_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_quality_gate_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_build_with_a11y_words": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_explain_with_review_terms": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_checklist_not_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_fix_only_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the feature-request chatter and run a pre-merge frontend review on this React diff for keyboard navigation, unsafe rendering, and missing empty states.", + "family": "adversarial_noisy_premerge_review", + "score": 0.876, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "ui_security", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.84, + "support_score": 0, + "lexical_support": 0.542, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "keyboard navigation" + ], + "frontend_scope": [ + "react", + "frontend" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "ui_security": [ + "unsafe rendering" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Treat this as a quality gate: review the component for a11y, form handling, and behavior regressions instead of rewriting it.", + "family": "adversarial_quality_gate_review", + "score": 0.544, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.5, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "component", + "form" + ], + "ui_security": [ + "form handling" + ], + "ux_regression": [ + "quality gate" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a React form with accessible labels, safe rendering, and polished empty states.", + "family": "adversarial_build_with_a11y_words", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "build_ui" + ], + "exclusive_negative_concepts": [ + "build_ui" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.385, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react", + "form" + ] + }, + "negative": { + "build_ui": [ + "build a react" + ] + } + } + } + }, + { + "prompt": "Explain keyboard navigation problems in this component and teach me the basics.", + "family": "adversarial_explain_with_review_terms", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "keyboard navigation" + ], + "frontend_scope": [ + "component" + ] + }, + "negative": { + "explain_only": [ + "teach me" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Brainstorm a reusable frontend review checklist for later, but do not inspect the code or run a review.", + "family": "adversarial_checklist_not_review", + "score": 0.035, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.438, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + }, + { + "prompt": "Debug this component's form handling bug and fix the unsafe rendering issue.", + "family": "adversarial_fix_only_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "fix_only" + ], + "exclusive_negative_concepts": [ + "fix_only" + ], + "semantic_coverage": 0.22, + "support_score": 0, + "lexical_support": 0.231, + "negative_penalty": 0.18, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ], + "ui_security": [ + "unsafe rendering", + "form handling" + ] + }, + "negative": { + "fix_only": [ + "debug this component" + ] + } + } + } + } + ] + } + }, + "baseline_adversarial_holdout_report": { + "threshold": 0.34, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_premerge_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_quality_gate_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_build_with_a11y_words": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_explain_with_review_terms": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_checklist_not_review": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_fix_only_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the feature-request chatter and run a pre-merge frontend review on this React diff for keyboard navigation, unsafe rendering, and missing empty states.", + "family": "adversarial_noisy_premerge_review", + "score": 0.863, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "ui_security", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.84, + "support_score": 0, + "lexical_support": 0.375, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "keyboard navigation" + ], + "frontend_scope": [ + "react", + "frontend" + ], + "review_frontend": [ + "frontend review", + "pre-merge frontend review" + ], + "ui_security": [ + "unsafe rendering" + ], + "ux_regression": [ + "pre-merge" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Treat this as a quality gate: review the component for a11y, form handling, and behavior regressions instead of rewriting it.", + "family": "adversarial_quality_gate_review", + "score": 0.544, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "ui_security", + "ux_regression" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.5, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "accessibility": [ + "a11y" + ], + "frontend_scope": [ + "component", + "form" + ], + "ui_security": [ + "form handling" + ], + "ux_regression": [ + "quality gate" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Build a React form with accessible labels, safe rendering, and polished empty states.", + "family": "adversarial_build_with_a11y_words", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "build_ui" + ], + "exclusive_negative_concepts": [ + "build_ui" + ], + "semantic_coverage": 0.04, + "support_score": 0, + "lexical_support": 0.231, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "react", + "form" + ] + }, + "negative": { + "build_ui": [ + "build a react" + ] + } + } + } + }, + { + "prompt": "Explain keyboard navigation problems in this component and teach me the basics.", + "family": "adversarial_explain_with_review_terms", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "accessibility", + "frontend_scope" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.24, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "accessibility": [ + "keyboard navigation" + ], + "frontend_scope": [ + "component" + ] + }, + "negative": { + "explain_only": [ + "teach me" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Brainstorm a reusable frontend review checklist for later, but do not inspect the code or run a review.", + "family": "adversarial_checklist_not_review", + "score": 0.025, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.38, + "support_score": 0, + "lexical_support": 0.312, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "frontend" + ], + "review_frontend": [ + "frontend review" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ] + } + } + } + }, + { + "prompt": "Debug this component's form handling bug and fix the unsafe rendering issue.", + "family": "adversarial_fix_only_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage", + "ux_regression", + "frontend_scope" + ], + "matched_desired_concepts": [ + "frontend_scope", + "ui_security" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "fix_only" + ], + "exclusive_negative_concepts": [ + "fix_only" + ], + "semantic_coverage": 0.22, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.18, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "frontend_scope": [ + "component", + "form" + ], + "ui_security": [ + "unsafe rendering", + "form handling" + ] + }, + "negative": { + "fix_only": [ + "debug this component" + ] + } + } + } + } + ] + } + }, + "candidates": [ + { + "id": "current", + "label": "Current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "strategy": "current", + "estimated_tokens": 50, + "dev": { + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 3, + -1.0, + -1.0, + -1.0, + -0.25, + 50 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "guardrail", + "label": "Guardrail", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "strategy": "guardrail_template", + "estimated_tokens": 62, + "dev": { + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 3, + -1.0, + -1.0, + -1.0, + -0.25, + 62 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "balanced", + "label": "Balanced", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "strategy": "balanced_template", + "estimated_tokens": 64, + "dev": { + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 3, + -1.0, + -1.0, + -1.0, + -0.25, + 64 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "artifact_aware", + "label": "Artifact Aware", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Trigger when requests mention React components, forms, frontend diffs, or UI code and the job is to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "strategy": "artifact_template", + "estimated_tokens": 84, + "dev": { + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 3, + -1.0, + -1.0, + -1.0, + -0.25, + 84 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "boundary", + "label": "Boundary", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "strategy": "boundary_template", + "estimated_tokens": 90, + "dev": { + "false_positives": 0, + "false_negatives": 3, + "precision": 1.0, + "recall": 0.25, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 3, + -1.0, + -1.0, + -1.0, + -0.25, + 90 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + } + ], + "selection_logic": { + "priority": [ + "fewest false positives", + "fewest false negatives", + "highest near-neighbor pass rate", + "highest negative pass rate", + "highest precision", + "highest recall", + "shortest description" + ] + }, + "comparison": { + "winner_vs_current_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 3, + "improved_false_positives": 0, + "improved_false_negatives": 3, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 0.25, + "improved_recall": 0.25 + }, + "winner_vs_current_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 3, + "improved_false_positives": 0, + "improved_false_negatives": 3, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 0.25, + "improved_recall": 0.25 + }, + "winner_vs_baseline_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + } + }, + "acceptance_gates": { + "selection_basis": "dev only", + "holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.089, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.496, + "max_non_trigger_score": 0.211, + "score_gap": 0.285, + "threshold_margin": 0.016, + "positive_threshold_buffer": 0.016, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "current_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.089, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.496, + "max_non_trigger_score": 0.211, + "score_gap": 0.285, + "threshold_margin": 0.016, + "positive_threshold_buffer": 0.016, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "baseline_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.679, + "mean_non_trigger_score": 0.087, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.489, + "max_non_trigger_score": 0.211, + "score_gap": 0.278, + "threshold_margin": 0.009, + "positive_threshold_buffer": 0.009, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "current_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "baseline_calibration": { + "threshold": 0.48, + "mean_positive_score": 0.674, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.223, + "min_positive_score": 0.492, + "max_non_trigger_score": 0.41, + "score_gap": 0.082, + "threshold_margin": 0.012, + "positive_threshold_buffer": 0.012, + "negative_threshold_buffer": 0.07, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "judge_blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "winner_calibration": null, + "current_calibration": null, + "baseline_calibration": null, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "adversarial_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "current_calibration": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "baseline_calibration": { + "threshold": 0.34, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.006, + "mean_near_neighbor_score": 0.013, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.025, + "score_gap": 0.519, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.315, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + } + }, + "summary": { + "winner_label": "Current", + "winner_tokens": 50, + "current_tokens": 50, + "winner_dev_total_errors": 3, + "current_dev_total_errors": 3, + "winner_holdout_total_errors": 0, + "current_holdout_total_errors": 0, + "winner_blind_holdout_total_errors": 0, + "current_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_total_errors": 0, + "current_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_total_errors": 0, + "current_adversarial_holdout_total_errors": 0, + "winner_judge_blind_agreement_rate": 1.0, + "winner_adversarial_risk_band": "healthy", + "winner_adversarial_score_gap": 0.509, + "candidate_count": 5, + "baseline_tokens": 52, + "baseline_dev_total_errors": 3, + "baseline_holdout_total_errors": 0, + "baseline_blind_holdout_total_errors": 0, + "baseline_judge_blind_holdout_total_errors": 0, + "baseline_adversarial_holdout_total_errors": 0 + } +} diff --git a/examples/team-frontend-review/optimization/reports/description_optimization.md b/examples/team-frontend-review/optimization/reports/description_optimization.md new file mode 100644 index 0000000..f9d604f --- /dev/null +++ b/examples/team-frontend-review/optimization/reports/description_optimization.md @@ -0,0 +1,64 @@ +# Frontend Review Description Optimization + +Winner: `Current` + +- current tokens: `50` +- winner tokens: `50` +- baseline tokens: `52` + +## Winner + +Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. + +## Candidate Ranking + +| Candidate | Tokens | Dev FP | Dev FN | Dev Near | Holdout FP | Holdout FN | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| `Current` | 50 | 0 | 3 | 1.0 | 0 | 0 | +| `Guardrail` | 62 | 0 | 3 | 1.0 | 0 | 0 | +| `Balanced` | 64 | 0 | 3 | 1.0 | 0 | 0 | +| `Artifact Aware` | 84 | 0 | 3 | 1.0 | 0 | 0 | +| `Boundary` | 90 | 0 | 3 | 1.0 | 0 | 0 | + +## Acceptance Gates + +| Gate | Winner FP | Winner FN | Current FP | Current FN | Baseline FP | Baseline FN | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Blind Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Judge Blind Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Adversarial Holdout | 0 | 0 | 0 | 0 | 0 | 0 | + +## Calibration + +| Gate | Winner Gap | Winner Risk | Winner Boundary Rate | Current Gap | Baseline Gap | +| --- | ---: | --- | ---: | ---: | ---: | +| Holdout | 0.285 | tight | 0.167 | 0.285 | 0.278 | +| Blind Holdout | 0.088 | tight | 0.333 | 0.088 | 0.082 | +| Adversarial Holdout | 0.509 | healthy | 0.0 | 0.509 | 0.519 | + +## Judge Blind Summary + +| Gate | Winner Agreement | Winner Mean Confidence | Current Agreement | Baseline Agreement | +| --- | ---: | ---: | ---: | ---: | +| Judge Blind Holdout | 1.0 | 0.703 | 1.0 | 1.0 | + +## Family Health + +| Gate | Winner Clean Families | Winner Weakest Family | Current Clean Families | Baseline Clean Families | +| --- | --- | --- | --- | --- | +| Holdout | 6/6 | security_and_a11y (0 errors) | 6/6 | 6/6 | +| Blind Holdout | 6/6 | blind_premerge (0 errors) | 6/6 | 6/6 | +| Judge Blind Holdout | 6/6 | blind_premerge (0 errors) | 6/6 | 6/6 | +| Adversarial Holdout | 6/6 | adversarial_quality_gate_review (0 errors) | 6/6 | 6/6 | + +## Selection Logic + +Ordered by: +- fewest false positives +- fewest false negatives +- highest near-neighbor pass rate +- highest negative pass rate +- highest precision +- highest recall +- shortest description diff --git a/examples/team-frontend-review/optimization/semantic_config.json b/examples/team-frontend-review/optimization/semantic_config.json new file mode 100644 index 0000000..a92eaed --- /dev/null +++ b/examples/team-frontend-review/optimization/semantic_config.json @@ -0,0 +1,156 @@ +{ + "optimizer_hints": { + "capability": "Review frontend code for accessibility, UI security, missing states, and UX regressions", + "inputs": [ + "React components", + "forms", + "frontend diffs", + "UI code" + ], + "trigger_actions": [ + "review React components", + "run a pre-merge frontend review", + "check a11y and unsafe rendering", + "check loading, error, and empty states" + ], + "exclusions": [ + "building a new page from scratch", + "backend-only review", + "explaining frontend concepts", + "brainstorming UI ideas" + ], + "artifacts": [ + "React components", + "forms", + "frontend diffs", + "UI code" + ] + }, + "fallback_positive_concepts": [ + "review_frontend", + "accessibility", + "ui_security", + "state_coverage" + ], + "positive_concepts": { + "review_frontend": { + "weight": 0.34, + "phrases": [ + "review frontend code", + "frontend review", + "review react components", + "review ui code", + "pre-merge frontend review", + "review this component" + ] + }, + "accessibility": { + "weight": 0.2, + "phrases": [ + "accessibility", + "a11y", + "keyboard navigation", + "screen reader" + ] + }, + "ui_security": { + "weight": 0.18, + "phrases": [ + "unsafe rendering", + "ui security", + "dangerouslysetinnerhtml", + "xss", + "form handling" + ] + }, + "state_coverage": { + "weight": 0.16, + "phrases": [ + "loading state", + "error state", + "empty state", + "missing states" + ] + }, + "ux_regression": { + "weight": 0.08, + "phrases": [ + "ux regression", + "behavior regression", + "pre-merge", + "quality gate" + ] + }, + "frontend_scope": { + "weight": 0.04, + "phrases": [ + "react", + "frontend", + "ui", + "component", + "form" + ] + } + }, + "negative_concepts": { + "build_ui": { + "weight": 0.35, + "exclusive": true, + "phrases": [ + "build a react", + "create a page", + "write a component", + "implement a ui", + "generate code" + ] + }, + "backend_only": { + "weight": 0.28, + "exclusive": true, + "phrases": [ + "backend review", + "database query", + "api endpoint", + "server code" + ] + }, + "explain_only": { + "weight": 0.24, + "exclusive": true, + "phrases": [ + "explain react", + "explain this concept", + "what does accessibility mean", + "teach me" + ] + }, + "brainstorm_only": { + "weight": 0.24, + "exclusive": true, + "phrases": [ + "brainstorm", + "design ideas", + "ui ideas", + "improve the ux" + ] + }, + "fix_only": { + "weight": 0.18, + "exclusive": true, + "phrases": [ + "fix this bug", + "debug this component", + "repair this form" + ] + }, + "translate_only": { + "weight": 0.35, + "exclusive": true, + "phrases": [ + "translate", + "into japanese", + "into french" + ] + } + } +} diff --git a/examples/team-frontend-review/raw-workflow.md b/examples/team-frontend-review/raw-workflow.md new file mode 100644 index 0000000..eec92a4 --- /dev/null +++ b/examples/team-frontend-review/raw-workflow.md @@ -0,0 +1,14 @@ +# Raw Workflow + +Before merging frontend code, our team manually checks: + +- accessibility issues +- obvious security mistakes +- risky form handling +- missing loading and error states + +People ask for this in many ways: + +- "Review this React component before merge" +- "Check this UI for a11y and security problems" +- "Do a frontend review using our standards" diff --git a/failures/README.md b/failures/README.md new file mode 100644 index 0000000..6284313 --- /dev/null +++ b/failures/README.md @@ -0,0 +1,18 @@ +# Failure And Anti-Pattern Library + +This directory captures recurring ways a skill can fail before, during, or after packaging. + +Use it for three things: + +- regression design +- author training +- description and boundary tuning + +Current case library: + +- `one-off-vs-reusable` +- `explain-not-package` +- `document-export-vs-agent-skill` +- `future-outline-vs-build` + +Each family now includes a `cases.json` file so the anti-pattern can run in automated regression instead of living only as prose. diff --git a/failures/document-export-vs-agent-skill/README.md b/failures/document-export-vs-agent-skill/README.md new file mode 100644 index 0000000..058bd2b --- /dev/null +++ b/failures/document-export-vs-agent-skill/README.md @@ -0,0 +1,28 @@ +# Document Export Vs Agent Skill + +## Symptom + +The request asks to package content as a document or improve a document, but the system mistakes it for agent-skill creation. + +## Anti-Pattern + +Reading "package" as "skill package" without checking the target artifact. + +## Example Prompt + +`Package this explanation as a document, not as an agent skill.` + +## Why It Fails + +- "package" is overloaded +- release notes, runbooks, and docs often look structurally similar to skills + +## Fix + +- model target artifact explicitly +- treat document-only exports as negative route evidence + +## Regression Prompts + +- `Improve this README but do not turn it into a skill.` +- `Package this explanation as a document, not as an agent skill.` diff --git a/failures/document-export-vs-agent-skill/cases.json b/failures/document-export-vs-agent-skill/cases.json new file mode 100644 index 0000000..2906459 --- /dev/null +++ b/failures/document-export-vs-agent-skill/cases.json @@ -0,0 +1,24 @@ +{ + "recommended_threshold": 0.33, + "should_trigger": [], + "should_not_trigger": [ + { + "text": "Rewrite this paragraph more clearly, but do not package anything.", + "family": "document_only" + } + ], + "near_neighbor": [ + { + "text": "Improve this README but do not turn it into a skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill" + }, + { + "text": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill" + } + ] +} diff --git a/failures/explain-not-package/README.md b/failures/explain-not-package/README.md new file mode 100644 index 0000000..405cb79 --- /dev/null +++ b/failures/explain-not-package/README.md @@ -0,0 +1,28 @@ +# Explain Not Package + +## Symptom + +The request is asking for explanation or interpretation, but the skill creator tries to package it as a reusable workflow. + +## Anti-Pattern + +Confusing topic adjacency with packaging intent. + +## Example Prompt + +`Review this note and tell me what it means in plain English.` + +## Why It Fails + +- the source material may look like a workflow +- the user still wants explanation, not systematization + +## Fix + +- treat explanation, summary-only, and translation-only modes as direct exclusions +- require positive evidence of reusable packaging intent before triggering + +## Regression Prompts + +- `Explain what a workflow is.` +- `Review this process note and explain it, no packaging needed.` diff --git a/failures/explain-not-package/cases.json b/failures/explain-not-package/cases.json new file mode 100644 index 0000000..c08be99 --- /dev/null +++ b/failures/explain-not-package/cases.json @@ -0,0 +1,24 @@ +{ + "recommended_threshold": 0.33, + "should_trigger": [], + "should_not_trigger": [ + { + "text": "Explain what a workflow is.", + "family": "explain_only" + }, + { + "text": "Review this note and tell me what it means in plain English.", + "family": "explain_only" + } + ], + "near_neighbor": [ + { + "text": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package" + }, + { + "text": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_not_package" + } + ] +} diff --git a/failures/future-outline-vs-build/README.md b/failures/future-outline-vs-build/README.md new file mode 100644 index 0000000..9160379 --- /dev/null +++ b/failures/future-outline-vs-build/README.md @@ -0,0 +1,28 @@ +# Future Outline Vs Build + +## Symptom + +The user is still framing or exploring a future skill, but the router prematurely triggers full skill generation. + +## Anti-Pattern + +Treating early planning language as execution-ready packaging intent. + +## Example Prompt + +`Create an outline for a possible future skill, but do not build the skill yet.` + +## Why It Fails + +- the prompt mentions "skill" +- the actual task is pre-build design, not packaging + +## Fix + +- detect "future skill", "outline", "before we decide", and similar wording as planning-only exclusions +- require either present-tense packaging intent or explicit build intent + +## Regression Prompts + +- `Help me shape an idea before we decide whether to build a skill.` +- `Create an outline for a possible future skill, but do not build the skill yet.` diff --git a/failures/future-outline-vs-build/cases.json b/failures/future-outline-vs-build/cases.json new file mode 100644 index 0000000..185ef86 --- /dev/null +++ b/failures/future-outline-vs-build/cases.json @@ -0,0 +1,19 @@ +{ + "recommended_threshold": 0.33, + "should_trigger": [], + "should_not_trigger": [], + "near_neighbor": [ + { + "text": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build" + }, + { + "text": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build" + }, + { + "text": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build" + } + ] +} diff --git a/failures/one-off-vs-reusable/README.md b/failures/one-off-vs-reusable/README.md new file mode 100644 index 0000000..bd515a4 --- /dev/null +++ b/failures/one-off-vs-reusable/README.md @@ -0,0 +1,28 @@ +# One-Off Vs Reusable + +## Symptom + +The router sees strong packaging language and triggers even though the user only wants a one-off answer or prompt. + +## Anti-Pattern + +Treating any "create" or "package" wording as reusable-skill intent. + +## Example Prompt + +`Write a custom answer for this request without creating a skill package.` + +## Why It Fails + +- lexical overlap is high +- true user intent is single-use output, not reusable infrastructure + +## Fix + +- detect one-off language explicitly +- treat "custom answer", "one-off", and similar phrases as exclusion concepts + +## Regression Prompts + +- `Create a one-off prompt for this task.` +- `Write a custom answer for this request without creating a skill package.` diff --git a/failures/one-off-vs-reusable/cases.json b/failures/one-off-vs-reusable/cases.json new file mode 100644 index 0000000..6d9056b --- /dev/null +++ b/failures/one-off-vs-reusable/cases.json @@ -0,0 +1,19 @@ +{ + "recommended_threshold": 0.33, + "should_trigger": [], + "should_not_trigger": [], + "near_neighbor": [ + { + "text": "Create a one-off prompt for this task.", + "family": "one_off_vs_reusable" + }, + { + "text": "Write a custom answer for this request without creating a skill package.", + "family": "one_off_vs_reusable" + }, + { + "text": "Give me a single-use prompt for today's task, not a reusable skill.", + "family": "one_off_vs_reusable" + } + ] +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..317a1a1 --- /dev/null +++ b/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "yao-meta-skill", + "version": "1.1.0", + "owner": "Yao Team", + "updated_at": "2026-03-31", + "status": "active", + "maturity_tier": "governed", + "lifecycle_stage": "library", + "context_budget_tier": "production", + "review_cadence": "quarterly", + "target_platforms": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "factory_components": [ + "templates", + "references", + "scripts", + "reports" + ] +} diff --git a/references/artifact-design-doctrine.md b/references/artifact-design-doctrine.md new file mode 100644 index 0000000..b53d4d3 --- /dev/null +++ b/references/artifact-design-doctrine.md @@ -0,0 +1,49 @@ +# Artifact Design Doctrine + +Use this layer when a skill produces user-facing artifacts: HTML reports, Markdown tutorials, review viewers, dashboards, screenshots, tables, slide-like pages, or generated skill overview pages. + +## Principle + +Output quality is part of skill quality. A generated skill should not only know what to do; it should also know how its final artifact should read, scan, and hold up under review. + +The visual system must follow the artifact's purpose. Do not inherit a fixed house style just because a reference skill uses one. For example, Kami's paper-editorial discipline is useful, but its warm parchment background is not a default requirement for Yao-generated artifacts. + +## What To Borrow From Document Skills + +- route by artifact type before designing the page +- extract facts, claims, numbers, actions, and missing inputs before formatting +- ask one focused question when the artifact lacks a necessary audience, input, or output standard +- keep prose, layout, and production checks separated +- verify placeholders, headings, paths, screenshots, and render-critical assumptions before delivery + +## What To Borrow From Presentation Skills + +- plan the artifact's role, hierarchy, density, rhythm, and evidence before writing HTML +- choose a concrete visual direction from the topic, not from a generic AI template +- use structure, spacing, type, and contrast before decoration +- split dense content instead of squeezing it into one surface +- reject generic purple gradients, glass cards, repeated card grids, and decorative screenshots + +## Content-Led Visual Direction + +Pick the design system from the work: + +- high-trust reports: restrained editorial layout, strong hierarchy, compact evidence blocks +- tutorials: clear progressive sections, success checks, screenshots only when real and necessary +- dashboards: compact metrics, visible deltas, short explanations, no paragraph-heavy tables +- review viewers: side-by-side comparison, reviewer-visible evidence, explicit tradeoffs +- slide-like artifacts: rhythm, section breaks, big claims, controlled density + +## Non-Negotiables + +- headings must be specific to the user's domain and outcome +- tables are used only when comparison is the main job +- citations and footnotes must not interrupt ordinary reading +- screenshots and visual evidence must be real, sourceable, and correctly described +- final HTML must not contain absolute local filesystem paths +- mobile and narrow-width reading must remain usable +- design tokens must be named and coherent: type, color, spacing, surface, and emphasis + +## Reviewer Rule + +Reviewers should see both the artifact's intended visual direction and the top risks that could make the output feel low quality. If a report looks polished but hides weak headings, bad tables, wrong screenshots, or citation clutter, the skill is not ready. diff --git a/references/authoring-discipline.md b/references/authoring-discipline.md new file mode 100644 index 0000000..02461ab --- /dev/null +++ b/references/authoring-discipline.md @@ -0,0 +1,78 @@ +# Authoring Discipline + +Use this discipline when creating, refactoring, or reviewing a skill package. It keeps the system useful without turning every request into a heavy framework. + +## Principle + +Every added instruction, file, script, evaluation, or governance rule must trace back to the user's real recurring job. + +## 1. Assumption Discipline + +Do not deepen the package on a guessed goal. + +- state the working assumption when the user's request has more than one plausible interpretation +- ask a short follow-up when the recurring job, target output, or exclusion boundary is unclear +- surface a real design conflict instead of silently choosing a risky direction +- proceed silently only when the decision is low-risk and reversible + +Good clarification is small. Ask the question that changes the package design, not a full intake form. + +## 2. Scope Discipline + +Build the smallest reliable package. + +- do not add features the user did not ask for or the workflow does not need +- do not add generic configurability before a real variation exists +- do not add empty folders, decorative reports, or broad policy text to look complete +- prefer one strong execution path over several speculative branches + +A package is not better because it has more files. It is better when the recurring job becomes clearer, safer, or easier to verify. + +## 3. Change Discipline + +When improving an existing skill, make surgical changes. + +- touch only files that directly support the requested change +- match the existing style and structure unless they are the problem +- remove unused artifacts created by the current change +- mention unrelated dead code or drift, but do not clean it up unless asked + +The review test: every changed line should explain which user goal, boundary, or verification need it serves. + +## 4. Verification Discipline + +Tie each meaningful change to a check. + +- trigger changes need route or near-neighbor evidence +- execution changes need a sample input, script check, or manual run note +- output-facing changes need an output risk profile and at least one self-repair check +- new references need a reason they reduce ambiguity or context cost +- borrowed patterns need recurrence, generativity, distinctiveness, or boundary evidence appropriate to the skill tier +- new governance needs an owner, lifecycle expectation, or review cadence +- new packaging or portability claims need a concrete target or compatibility check + +If a change cannot be verified yet, label it as a candidate next step instead of shipping it as part of the baseline package. + +## Reviewer Checklist + +Before approving a generated or modified skill, check: + +- the real recurring job is explicit +- unresolved assumptions are named or clarified +- the package is no larger than the job requires +- changes are limited to the requested scope +- each new artifact has a verification reason +- borrowed patterns have reviewer-visible acceptance evidence +- likely output mistakes are named before examples are approved +- the next iteration direction is focused, not a bundle of speculative upgrades + +## Failure Patterns + +Treat these as authoring failures: + +- creating a skill for a one-off answer +- adding scripts when prose is enough +- adding evals before route risk exists +- adding governance to a personal scaffold with no reuse pressure +- modifying sibling files because they looked related +- presenting a recommendation without naming the assumption behind it diff --git a/references/autonomous-adaptation.md b/references/autonomous-adaptation.md new file mode 100644 index 0000000..6f3684d --- /dev/null +++ b/references/autonomous-adaptation.md @@ -0,0 +1,65 @@ +# Autonomous Adaptation Method + +This reference defines the safe foundation for adaptive self-iteration. + +## Scope + +Adaptive iteration is proposal-only until a human explicitly approves a patch application workflow. The current implementation may: + +- read one user-provided local source file; +- redact sensitive text before storing evidence excerpts; +- summarize repeated preferences and operational signals; +- produce adaptation proposals with target files, risks, tests, and rollback plans. +- draft a pending approval ledger entry from a reviewed patch, including patch SHA-256, target files, target baseline hashes, verifier commands, and rollback metadata. +- dry-run an approved patch through `adapt-apply`, after patch hash, approval, target allowlist, and target baseline hash checks pass. +- apply a patch only when the operator passes `--apply` and the approval ledger names the reviewer, reason, patch hash, target files, target file SHA-256 baselines, verification commands, and rollback plan. +- automatically reverse an applied patch when `--run-verification` fails, unless the operator explicitly passes `--no-rollback-on-failure`. + +It must not: + +- scan shell history, browser history, chat logs, mail, or private folders by default; +- infer permanent user memory from a single comment; +- write source files as part of scan or proposal generation; +- write source files through `adapt-apply` without explicit `--apply`; +- apply a patch whose target files are outside both the proposal and approval allowlists; +- apply a patch when an approved target file has changed since the reviewer recorded its baseline SHA-256; +- leave a failed verified apply in place by default; +- count proposals as completed implementation evidence. + +## Flow + +1. `adapt-scan` reads an explicit source path and writes `reports/user_patterns.json` plus `reports/user_patterns.md`. +2. `adapt-propose` reads the pattern report and writes `reports/adaptation_proposals.json` plus `reports/adaptation_proposals.md`. +3. A reviewer decides whether any proposal is worth implementing. +4. `adapt-apply --write-template` creates `reports/adaptation_approval_ledger.json` and `reports/adaptation_regression_report.json` so the review surface exists before any patch is applied. +5. `adapt-apply --prepare-approval --proposal-id --patch-file ` drafts a `pending-review` approval entry. It does not approve or apply the patch. +6. A human reviewer changes the draft decision to `approved`, fills reviewer, reason, approval date, and optional expiry, then keeps the generated patch and target baseline hashes intact. +7. `adapt-apply --proposal-id --patch-file ` defaults to a dry-run and records patch, target, approval, regression, and rollback evidence. +8. `adapt-apply --apply --run-verification` may write files only after approval, patch hash, allowlist, target baseline hash, `git apply --check`, and safe regression command checks pass. +9. If a verification command fails after a patch is applied, `adapt-apply` runs `git apply -R ` by default and records `failed-rolled-back` plus rollback evidence in `reports/adaptation_regression_report.json`. + +## Evidence Standard + +Each proposal should include: + +- the repeated pattern that triggered it; +- redacted excerpts, never unredacted raw content; +- target files and change intent; +- risk level and boundary; +- verification commands; +- rollback plan; +- a clear `proposal-only` status. + +Each approved application should include: + +- reviewer, reason, approval date, and optional expiry; +- exact patch SHA-256; +- target file allowlist; +- target file SHA-256 baselines for every patch target, or `__absent__` for approved new files; +- regression commands restricted to local `make` targets or local Python verifier scripts; +- rollback command or plan. +- rollback result if regression failed after an apply attempt. + +## Review Boundary + +The adaptive loop improves iteration quality, but it does not replace normal review. Any proposal touching trigger behavior, reports, packaging, telemetry, privacy, or governance must still pass the same tests and release gates as a manually designed change. `adapt-apply` evidence proves that an approved patch path was checked or applied; it does not make world-class external or human evidence complete. diff --git a/references/distribution-registry-method.md b/references/distribution-registry-method.md new file mode 100644 index 0000000..4511b58 --- /dev/null +++ b/references/distribution-registry-method.md @@ -0,0 +1,60 @@ +# Distribution Registry Method + +Registry metadata turns a local skill directory into an installable and reviewable package record. + +## When To Use + +Use registry audit for library, governed, team-distributed, or upgrade-sensitive skills. Scaffold skills can skip it until reuse is real. + +## Required Evidence + +- package name, version, owner, maturity, review cadence, and license +- Skill IR source and schema version +- trust level and package SHA256 +- package verification status and archive SHA256 when a zip distribution is built +- install simulation status when an archive is meant to be installed locally or by a team +- adoption/drift aggregate status when local telemetry exists +- target compatibility matrix +- upgrade diff, recommended version bump, and breaking-change notes when a previous package baseline exists +- links to overview, Review Studio, trust report, conformance matrix, package verification, install simulation, adoption drift evidence, and review waivers + +## Release Rule + +Do not publish a team package when registry audit reports missing version, hash, owner, review cadence, license, valid Skill IR, or passing compatibility for declared targets. + +For an installable archive, first build the distribution and run package verification: + +```bash +python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip +python3 scripts/yao.py package-verify . --package-dir dist --require-zip +python3 scripts/yao.py install-simulate . --package-dir dist +python3 scripts/yao.py registry-audit . +python3 scripts/yao.py upgrade-check . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json +``` + +Do not claim archive readiness when package verification reports unsafe zip paths, missing target adapters, missing package manifest, registry metadata mismatch, or unreadable archive contents. + +Do not claim install readiness when install simulation cannot extract the archive into a temporary skill root, load `SKILL.md` frontmatter, read `manifest.json`, read `agents/interface.yaml`, find the overview and Review Studio reports, or load each generated adapter. + +Do not sync a local or active install from source until the same package has passed install preflight. `scripts/sync_local_install.py` must run install simulation against the configured package directory and fail before copying files when any target/capability pair lacks active permission approval or target-specific enforcement evidence. Use `--skip-install-preflight` only for isolated diagnostics, not for release or active install. + +Do not include raw `reports/telemetry_events.jsonl` in a distributed package. Include only aggregate adoption drift reports, and block release review when telemetry contains raw prompts, outputs, transcripts, notes, or messages. + +Review waiver evidence may be distributed as `reports/review_waivers.md/json` because it is metadata-only reviewer accountability. Do not store raw prompts, outputs, transcripts, credentials, or private customer detail in waiver reasons. + +Do not claim upgrade readiness when upgrade check reports an insufficient version bump, target removal without a major bump, compatibility regression without a major bump, package name change, or invalid semver. Include `reports/upgrade_check.md` in the reviewer evidence bundle so release notes and migration guidance are tied to the exact registry diff. + +## Reviewer Gate + +A reviewer should be able to answer: + +1. Which package version is being installed? +2. Who owns the package? +3. Which targets are compatible? +4. Which checksum identifies the reviewed package contents? +5. Which reports prove trust and runtime readiness? +6. Was the installable archive verified, and which checksum identifies it? +7. Was the archive install-simulated in a temporary local skill root? +8. Did local or active install sync preserve that preflight and installer permission gate? +9. What changed since the previous package, and does the declared version bump match the recommended bump? +10. Are adoption and drift signals summarized without packaging raw local telemetry? diff --git a/references/eval-playbook.md b/references/eval-playbook.md new file mode 100644 index 0000000..b5fa83d --- /dev/null +++ b/references/eval-playbook.md @@ -0,0 +1,69 @@ +# Trigger And Eval Playbook + +Use this playbook for skills that matter enough to test. + +## A. Trigger Evaluation + +Create three prompt buckets: + +### 1. Should Trigger + +Prompts that clearly need the skill. + +Goal: + +- verify recall + +### 2. Should Not Trigger + +Prompts that are clearly outside the skill boundary. + +Goal: + +- verify precision + +### 3. Near Neighbors + +Prompts that look similar but should use another skill or no skill. + +Goal: + +- catch false positives and ambiguous routing + +## B. Execution Evaluation + +For each important use case, create 1 to 3 realistic prompts with: + +- user-like phrasing +- representative inputs or file types +- expected output description +- key checks + +## C. Revision Loop + +When a skill underperforms: + +1. Fix boundary or description problems before adding more body text. +2. Move brittle logic into scripts or templates. +3. Split reference content if `SKILL.md` becomes bloated. +4. Re-run the same eval set before expanding scope. + +## D. Minimum QA By Skill Tier + +### Personal skill + +- 2 realistic prompts +- manual review + +### Team skill + +- 3 to 5 realistic prompts +- trigger positives and negatives +- one revision loop + +### Infrastructure or meta-skill + +- 5+ execution prompts +- trigger positives, negatives, and near neighbors +- benchmark notes across revisions +- ownership and drift review diff --git a/references/gate-selection.md b/references/gate-selection.md new file mode 100644 index 0000000..0200441 --- /dev/null +++ b/references/gate-selection.md @@ -0,0 +1,68 @@ +# Gate Selection + +Gate selection should track risk. More gates are not automatically better. + +## Minimum Gates By Risk + +| Risk Level | Typical Archetype | Required Gates | +| --- | --- | --- | +| low | Scaffold | `validate_skill.py`, `resource_boundary_check.py` | +| medium | Production | low-risk gates plus `trigger_eval.py` | +| high | Library | medium-risk gates plus `optimize_description.py`, route confusion, packaging validation | +| critical | Governed | high-risk gates plus governance scoring, regression history, promotion policy | + +## Trigger Gates + +Use trigger gates when: + +- a route can be confused with a nearby skill +- the workflow will be reused by multiple people +- the skill boundary must be defended over time + +Trigger gates, in order: + +1. `trigger_eval.py` +2. `optimize_description.py` +3. blind holdout +4. judge-backed blind holdout +5. adversarial holdout +6. route confusion + +## Packaging Gates + +Use packaging gates when: + +- output must be consumed by multiple clients +- metadata drift would break discoverability +- portability is part of the skill's value + +Packaging gates: + +- `validate_skill.py` +- `cross_packager.py` +- packager failure fixtures +- adapter snapshot checks + +## Governance Gates + +Use governance gates when: + +- the skill is shared infrastructure +- the skill has an owner and review cadence +- lifecycle state matters + +Governance gates: + +- `governance_check.py` +- regression history +- maturity scoring +- promotion policy + +## Context Gates + +Use context gates on every non-trivial skill. + +- `context_sizer.py` +- `resource_boundary_check.py` + +If rigor increases but initial load crosses the tier budget, move detail into `references/`, `scripts/`, or `reports/` before adding more checks. diff --git a/references/governance.md b/references/governance.md new file mode 100644 index 0000000..17b5992 --- /dev/null +++ b/references/governance.md @@ -0,0 +1,134 @@ +# Governance Model + +This project treats important skills as governed assets rather than one-shot prompt files. + +## Goals + +- keep shared skills trustworthy over time +- make ownership explicit +- avoid stale or oversized skill packages +- define when a skill should evolve, split, or retire + +## Required Governance Metadata + +For reusable or library-grade skills, `manifest.json` should include: + +- `name` +- `version` +- `owner` +- `updated_at` +- `review_cadence` +- `status` +- `maturity_tier` +- `lifecycle_stage` + +## Allowed Values + +### `status` + +- `experimental` +- `active` +- `deprecated` + +### `maturity_tier` + +- `scaffold` +- `production` +- `library` +- `governed` + +### `lifecycle_stage` + +- `scaffold` +- `production` +- `library` +- `governed` + +### `review_cadence` + +- `monthly` +- `quarterly` +- `semiannual` +- `annual` +- `per-release` + +## Governance Rules + +### 1. Owner Required + +Any skill meant for reuse must have a named owner or owning team. + +### 2. Review Cadence Required + +If a skill is shared, it must declare how often it should be reviewed. + +### 3. Maturity Should Match Rigor + +- `scaffold`: lightweight, personal, low-governance +- `production`: reusable team skill with validation +- `library`: curated shared skill with explicit packaging and evals +- `governed`: critical or meta-level skill with regression, maintenance, and review expectations + +### 4. Deprecated Skills Need Explicit Intent + +Deprecated skills should include a deprecation note or replacement reference in adjacent documentation or manifest extensions. + +### 5. Drift Must Be Observable + +Important skills should keep: + +- a regression history +- visible evaluation results +- known anti-patterns or failure modes + +## Governance Actions + +Use governance review to decide whether a skill should: + +- stay as-is +- tighten trigger boundaries +- split into sibling skills +- move detail into `references/` +- move brittle logic into `scripts/` +- be deprecated or replaced + +## Governance Maturity Scoring + +`scripts/governance_check.py` also computes a maturity score out of `100`. + +### Score Buckets + +The governance checker computes a score band in addition to the declared manifest tier. The score band is a diagnostic output, not a replacement for the declared lifecycle tier. + +- `90-100`: governed +- `80-89`: production +- `65-79`: reusable +- `45-64`: emerging +- `<45`: draft + +### Recommended Minimums For Declared Tiers + +- `scaffold`: no hard minimum +- `production`: `80` +- `library`: `85` +- `governed`: `90` + +### Score Dimensions + +- metadata integrity +- ownership and review cadence +- boundary and eval evidence +- operational assets +- maintenance evidence + +The score is not a replacement for human review. It is a fast signal that a shared skill is structured enough to be trusted, maintained, and audited. `scripts/governance_check.py` warns when a declared tier claims more rigor than the score currently supports. + +## Why This Matters + +Most skill systems stop at creation. World-class skill systems also manage: + +- ownership +- drift +- maturity +- deprecation +- evidence of ongoing quality diff --git a/references/human-review-template.md b/references/human-review-template.md new file mode 100644 index 0000000..8108bc8 --- /dev/null +++ b/references/human-review-template.md @@ -0,0 +1,54 @@ +# Human Review Template + +Use this template when a description candidate is ready for human review after automated gates have run. + +## Review Header + +- target: +- review date: +- reviewer: +- current description: +- candidate description: +- promotion decision: + +## What Changed + +- what was the intended improvement? +- what route boundary is now clearer? +- what family or holdout behavior improved? + +## Automated Evidence + +- visible holdout result: +- blind holdout result: +- judge-backed blind result: +- adversarial result: +- route confusion result: +- calibration / risk band: + +## Boundary Questions + +- does the candidate trigger only when a reusable skill package is actually needed? +- does it avoid one-off explanation, translation, or summary-only requests? +- does it avoid stealing sibling routes? +- does it stay short enough for the target maturity tier? + +## Authoring Discipline + +- are unresolved assumptions named or clarified before deepening the package? +- is the package no larger than the real recurring job requires? +- do changed files trace directly to the requested improvement? +- did the author avoid speculative features, decorative folders, and generic configuration? +- does each new artifact have a verification reason? + +## Decision + +- approve promotion +- keep current +- block candidate + +## Notes + +- rationale: +- follow-up actions: +- next review trigger: diff --git a/references/intent-dialogue.md b/references/intent-dialogue.md new file mode 100644 index 0000000..0d361e8 --- /dev/null +++ b/references/intent-dialogue.md @@ -0,0 +1,138 @@ +# Intent Dialogue + +Use a short, human conversation before deep authoring so the first version of the skill is anchored in the real job rather than in a guessed prompt shape. + +## Why This Step Exists + +- raw workflow material is often incomplete, mixed, or ambiguous +- the wrong boundary chosen early is expensive to repair later +- good trigger design depends on knowing what should not route here +- execution assets should follow confirmed outputs, not assumptions + +## What To Capture + +Ask only the questions that change the package design. + +1. If this worked beautifully, what recurring job would it quietly take off the user's plate? +2. What real inputs would people actually hand to it? +3. What finished output should it hand back so the user can keep moving? +4. What near-neighbor requests should it politely refuse? +5. What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit? +6. Are there any public or private references the user wants this skill to learn from? Only borrow patterns, never copy wording or private material. +7. What assets already exist: docs, scripts, templates, examples, or prior prompts? +8. What constraints matter: privacy, naming, local library fit, or target environments? + +## Interview Rule + +- prefer `5-7` sharp questions over a long discovery questionnaire +- start with a calm, human framing before switching into precise design questions +- guide like a patient teacher or thoughtful coach, not like a rigid intake clerk +- mirror the user's language and emotional temperature +- first invite a natural explanation, then offer a lightweight template only as an option +- ask boundary questions early +- ask output questions before architecture questions +- stop once the skill can be described clearly in one sentence +- do not enter deep authoring until the recurring job, target output, and exclusion boundary are clear enough to defend + +## First Message Pattern + +The first message should feel like guided co-creation, not form filling. + +Recommended flow: + +1. briefly acknowledge the user's seed idea +2. explain that you want to first understand the real recurring work and what a good outcome looks like +3. invite the user to describe it naturally in their own words +4. offer a tiny scaffold only if they want a shortcut + +Good example shape: + +- `Let's make this easy. Tell me what kind of repeated work you want this skill to quietly take over, what people will hand to it, and what a useful finished result should look like. If you want, I can also give you a tiny template to fill in.` + +Warmer guidance: + +- sound like you are sitting beside the user, helping them sort out a half-formed idea +- do not rush into system terms such as `archetype`, `gate`, or `package` in the first breath +- name the user's possible feeling: fuzzy, scattered, not fully formed, hard to describe +- make it feel safe to answer imperfectly +- offer to help extract structure after they speak naturally + +Bad example shape: + +- `Name:` +- `One-line capability:` +- `Real input:` +- `Target output:` + +The second pattern is allowed only when the user explicitly asks for a structured template. + +## Chinese First-Turn Opening Patterns + +Use these as tone references when the conversation is in Chinese. Do not copy them mechanically; adapt them to the user's context and voice. + +### 温柔陪伴型 + +适合:用户想法还比较模糊,或者需要先被接住。 + +示例: + +- `我们先别急着定结构,你就像跟我聊天一样说说看:你最想让这个 skill 以后帮你稳稳接住哪一类重复工作?它如果做得很理想,最后应该交回你一个什么样的结果?` +- `没关系,现在不完整也可以。你先把脑子里已经有的部分告诉我,我来帮你一点点收拢成一个清晰的 skill。` +- `你可以先说个大概,比如“它以后主要帮我处理什么”、“别人通常会丢给它什么材料”、“我希望它最后产出什么”,剩下的我再陪你一起补齐。` + +### 专业教练型 + +适合:用户目标明确,希望被高效带着走,但仍然不想面对生硬表单。 + +示例: + +- `我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它最核心要接住的重复任务是什么,别人会给它什么输入,最后你希望它交付什么结果。` +- `我先不让你填模板。你先用自己的话说说:这件事做成以后,最重要的价值是什么,哪些相近请求你反而不希望它处理。` +- `先把业务和结果说清楚,结构我来替你提炼。你只要告诉我:它该做什么、不该做什么、做好以后对你有什么帮助。` + +### 共创伙伴型 + +适合:用户有一定想法,希望一起打磨,而不是被问卷式采集。 + +示例: + +- `我们把它当成一次共创来做。你先说说这个 skill 最值得被做出来的地方是什么,我再帮你把边界、输入和输出慢慢收成一个可复用的包。` +- `你可以先丢给我一个粗糙版本,不用一次说完整。我会先帮你看它真正的核心任务是什么,再一起决定要不要加规则、脚本或评测。` +- `如果你愿意,我们可以先从“理想中的它能帮你省掉什么麻烦”开始聊,然后再往下收敛成 skill 的能力边界。` + +### Lightweight Optional Scaffold + +Only offer this after the natural opening, not before. + +示例: + +- `如果你懒得一点点讲,我也可以给你一个很小的版本,你只填这几项就行:它最想接住的事、常见输入、理想输出、明确不做什么。` +- `如果你更习惯结构化一点,我可以把问题收成 4 行小模板;如果你想自然讲,也完全可以直接说。` + +## Output + +The dialogue should produce: + +- one clear capability sentence +- a list of real inputs +- a list of required outputs +- a short exclusion list +- a note on user-supplied references or benchmark preferences +- one recommended archetype +- one recommended first evaluation target + +## Failure Pattern + +Do not continue into full authoring when the dialogue still leaves these unresolved: + +- whether the request is really reusable +- which near-neighbor requests should not trigger +- what concrete deliverable the skill must return + +If one of these is unresolved, ask the smallest possible follow-up that will unlock the design. Do not compensate by adding extra references, scripts, or governance. + +Also treat these as dialogue failures: + +- the first reply feels like a cold worksheet instead of a guided conversation +- the user is forced into a full template before the real job is understood +- the assistant asks for package structure before clarifying the desired outcome diff --git a/references/iteration-philosophy.md b/references/iteration-philosophy.md new file mode 100644 index 0000000..04a24f8 --- /dev/null +++ b/references/iteration-philosophy.md @@ -0,0 +1,30 @@ +# Iteration Philosophy + +The first skill package is a routeable baseline, not the final answer. + +## Principle + +Improve the skill by the smallest change that increases reliability more than it increases context cost. + +## What Good Iteration Looks Like + +- tighten boundary before adding prose +- improve description before enlarging the file tree +- add references only when they remove ambiguity +- add scripts only when deterministic logic repeats +- add gates only when risk justifies maintenance cost +- prefer one strong next step over five vague upgrades + +## Default Priority Order + +1. trigger clarity and exclusions +2. execution assets that remove repeated manual work +3. promotion, governance, and portability only when reuse justifies them + +## First-Version Rule + +After the initial package is created, always surface the three highest-value next iteration directions. This keeps the author focused on the best improvement path instead of expanding the skill in every direction at once. + +## Anti-Pattern + +Do not treat iteration as "make the package bigger." A larger package is only better when routing, execution, or governance becomes materially more reliable. diff --git a/references/non-skill-decision-tree.md b/references/non-skill-decision-tree.md new file mode 100644 index 0000000..c50e8f2 --- /dev/null +++ b/references/non-skill-decision-tree.md @@ -0,0 +1,39 @@ +# Non-Skill Decision Tree + +Do not build a skill when the user really wants a direct answer or a lightweight artifact. + +## Use A Direct Answer Instead + +Prefer a normal response when the job is: + +- explain a concept +- summarize a document +- translate text +- brainstorm ideas without packaging +- answer a one-off question + +## Use A Document Instead + +Prefer a document or note when the output is: + +- wiki or knowledge-base content +- a reusable-looking document with no agent execution +- a policy memo +- a handoff note + +## Use A Script Instead + +Prefer a script without a full skill when: + +- the task is deterministic +- routing is not the hard part +- the user wants a utility, not a routed capability + +## Promote To A Skill Only When + +- the job is recurring +- discoverability matters +- the workflow needs a boundary +- the process benefits from reusable instructions or checks + +If there is doubt, start with `Scaffold` and upgrade only after reuse becomes real. diff --git a/references/operating-modes.md b/references/operating-modes.md new file mode 100644 index 0000000..a6e3c9d --- /dev/null +++ b/references/operating-modes.md @@ -0,0 +1,107 @@ +# Operating Modes + +This playbook expands the compact mode routing in `SKILL.md`. + +## Scaffold + +Use when: + +- the skill is exploratory +- the workflow is personal or short-lived +- eval and packaging cost would exceed reuse value + +Default deliverables: + +- `SKILL.md` +- `agents/interface.yaml` +- `references/` only when a small amount of deferred reading is clearly helpful + +Avoid: + +- automatic `scripts/`, `evals/`, or `manifest.json` +- packaging targets the user did not ask for + +## Production + +Use when: + +- the skill will be reused by a team +- routing mistakes would waste time +- a small amount of deterministic automation improves reliability + +Default deliverables: + +- lean `SKILL.md` +- `agents/interface.yaml` +- `references/` for policies, checklists, or examples +- `scripts/` only when deterministic logic is real +- `evals/` when trigger or output quality should be checked +- `manifest.json` when lifecycle metadata matters + +Minimum gates: + +- `resource_boundary_check.py` +- `validate_skill.py` +- `trigger_eval.py` when route confusion is plausible + +## Library + +Use when: + +- the skill is organizationally important +- the package will be shared broadly +- maintenance and portability matter +- the skill itself shapes how other skills are created or governed + +Default deliverables: + +- trigger positives, negatives, and near neighbors +- packaging expectations +- maintenance metadata +- visible regression evidence +- governance review readiness + +Minimum gates: + +- `resource_boundary_check.py` +- `governance_check.py` +- `trigger_eval.py` +- `cross_packager.py` for requested targets + +## Governed + +Use when: + +- the skill affects incident, release, compliance, security, or organizational standards +- external distribution, public claims, or high-permission scripts require reviewable evidence +- wrong output or wrong activation can cause operational, legal, trust, or reputational harm + +Default deliverables: + +- everything required for Library +- explicit owner, lifecycle, review cadence, and expiry-aware approvals +- trust/security reports for scripts, dependencies, permissions, secrets, and package hash +- output eval evidence with blind review status and reviewer-visible boundaries +- world-class or public-claim evidence ledger when public readiness is claimed + +Minimum gates: + +- Library gates +- `trust_check.py` +- runtime permission probes for packaged adapters +- review waiver ledger for accepted warning-level risk +- Review Studio before release +- claim guard before public world-class language + +## Escalation Rules + +- stay in Scaffold unless reuse is clearly real +- move to Production when team reuse or route confusion matters +- move to Library when the skill becomes shared infrastructure +- move to Governed when the skill needs explicit risk ownership, high-permission review, or public-claim evidence + +## Context Discipline + +- a mode upgrade does not justify a larger `SKILL.md` +- higher rigor should mostly add better references, reports, evals, and metadata +- if a mode upgrade bloats the initial load, move detail out before adding more checks diff --git a/references/output-eval-method.md b/references/output-eval-method.md new file mode 100644 index 0000000..dfb8d44 --- /dev/null +++ b/references/output-eval-method.md @@ -0,0 +1,113 @@ +# Output Eval Method + +Output Eval Lab proves whether a skill improves the final user-facing result, not only whether it routes correctly. + +## When To Use + +Use output evals for production, library, governed, or team-distributed skills. Scaffold skills can start with one smoke case, but production and above should show a positive with-skill vs baseline signal before promotion. + +## Case Design + +Each case should include: + +- a real prompt or task shape +- any required input files +- a baseline output that represents doing the task without the skill +- a with-skill output that represents the skill-guided behavior +- assertions that can be checked without subjective guessing +- optional human review notes for taste, completeness, or judgment + +## Assertion Rules + +Prefer assertions that catch material quality: + +- required deliverable paths +- required sections or contracts +- required boundary or exclusion language +- required evidence paths +- forbidden generic placeholders +- forbidden unsafe actions + +Avoid assertions that only reward wording memorization. If a case can pass by parroting one phrase while failing the real job, the assertion is too narrow. + +## Score Reading + +The first v0 scorecard reports: + +- baseline pass rate +- with-skill pass rate +- absolute delta +- failed assertions and failure taxonomy +- execution mode, timing, and token evidence when `reports/output_execution_runs.md` is generated +- blind A/B review pack count +- recommended next fixes + +Production promotion should require the with-skill pass rate to beat baseline and should explain every failed assertion. + +## Execution Evidence + +Run execution evidence after the scorecard: + +```bash +python3 scripts/yao.py output-exec +``` + +By default, this records the current case outputs as `recorded_fixture`. That is useful for reproducibility, but it is not model-executed evidence. To collect real run evidence, pass `--runner-command` with a command or JSON string list. The runner receives a JSON request on stdin and should return JSON with: + +- `output` +- optional `execution_kind`: `command` or `model` +- optional `provider` and `model` +- optional `usage.input_tokens`, `usage.output_tokens`, and `usage.total_tokens` + +Only runs that return provider/model metadata or `execution_kind: "model"` should count as model-executed. If token usage is absent, the report may estimate tokens, but the estimate must be labeled as estimated. + +For local release-gate smoke evidence without external model credentials, use the deterministic runner: + +```bash +python3 scripts/yao.py output-exec --runner-command '["python3","scripts/local_output_eval_runner.py"]' +``` + +This verifies the command-runner contract, timing capture, grading path, and failure handling. It must not be described as provider-backed model evidence. + +For provider-backed evidence, use the bundled provider runner with real credentials: + +```bash +YAO_OUTPUT_EVAL_MODEL=gpt-4.1-mini \ +OPENAI_API_KEY=... \ +python3 scripts/yao.py output-exec --provider-runner openai +``` + +The provider runner calls an OpenAI Responses API compatible endpoint, reads input files relative to `evals/output/`, returns `execution_kind: "model"`, and records observed token usage when the provider returns usage fields. If the API key or model is missing, the runner must fail instead of falling back to fixtures or pretending model evidence exists. Use `--provider-base-url` only for reviewed compatible endpoints; non-default HTTPS hosts require `--allow-custom-base-url`, and plain HTTP is allowed only with `--allow-insecure-localhost` for local test servers. + +## Blind A/B Review + +Every output eval run should also generate: + +- `reports/output_blind_review_pack.md` +- `reports/output_blind_review_pack.json` +- `reports/output_blind_answer_key.json` + +The review pack must hide whether Variant A or Variant B came from the baseline or the skill-guided output. The answer key is separate audit evidence and should only be opened after a reviewer has made a judgment. + +## Reviewer Adjudication + +After blind review, record reviewer choices in `reports/output_review_decisions.json` with `reviewer`, `reviewed_at`, `winner_variant`, optional `confidence`, and a required rubric-based `reason`, then run: + +```bash +python3 scripts/adjudicate_output_review.py --write-template +python3 scripts/yao.py output-review +``` + +The adjudication report writes: + +- `reports/output_review_decisions.json` +- `reports/output_review_adjudication.json` +- `reports/output_review_adjudication.md` + +When no reviewer decisions exist, the report should say the cases are pending and Review Studio should link to the decisions template. Do not count pending cases as human agreement. Only a real `winner_variant` of `A` or `B` with reviewer metadata and a non-empty `reason` should contribute to agreement rate, disagreement count, and reviewer judgment count. + +The adjudication report must preserve blind-review integrity: pending and invalid decisions should show the expected winner as hidden. Only reveal `expected_winner_variant` after a valid reviewer decision with rationale exists for that case. + +## Anti-Overfitting + +Keep a small public smoke set and a separate holdout set. Rotate real failures into the taxonomy instead of editing only the prompt that failed. Add near-neighbor cases whenever the output looks good but the boundary is still unclear. diff --git a/references/output-quality-risk.md b/references/output-quality-risk.md new file mode 100644 index 0000000..29f168f --- /dev/null +++ b/references/output-quality-risk.md @@ -0,0 +1,41 @@ +# Output Quality Risk + +Use this layer when a generated skill produces user-facing artifacts such as tutorials, reports, Markdown pages, screenshots, tables, code snippets, or research summaries. + +## Principle + +A skill is not complete when it can route and execute. It also needs to predict how its final output can fail in small but visible ways, then constrain those failures before the user sees them. + +## Common Failure Modes + +- generic headings that make a tutorial feel templated +- dense footnotes or citation markers that interrupt reading +- tables that render poorly or hide decisions inside long cells +- screenshot references that point to the wrong state, crop, or missing asset +- polished summaries that lose the user's actual audience or scenario +- commands or snippets that omit working directory, inputs, outputs, or side effects + +## Required Author Behavior + +Before finalizing a generated skill: + +1. infer the most likely output families from the job and target output +2. generate `reports/output-risk-profile.md` +3. generate `reports/artifact-design-profile.md` when the output is a report, tutorial, viewer, dashboard, screenshot, Markdown page, or visual artifact +4. add output-specific constraints to the generated skill's operating frame +5. expose the risk and design profiles in the review viewer +6. treat unresolved output risks as iteration candidates instead of pretending the first version is complete + +## Self-Repair Rule + +Every output-facing skill should do a final pass for: + +- specificity: headings, titles, and summaries fit the actual domain +- readability: Markdown, tables, and lists remain pleasant to scan +- evidence hygiene: citations support real claims without clutter +- visual truthfulness: screenshots and images are real, relevant, and correctly described +- execution clarity: commands and snippets name their assumptions and expected results + +## Reviewer Rule + +Reviewers should approve the skill only when the likely output mistakes are visible and the generated package contains a reasonable self-repair path for the highest-risk family. diff --git a/references/output-visual-quality.md b/references/output-visual-quality.md new file mode 100644 index 0000000..4f121e0 --- /dev/null +++ b/references/output-visual-quality.md @@ -0,0 +1,53 @@ +# Output Visual Quality + +Use this checklist before approving a generated skill that produces reports, tutorials, HTML pages, screenshots, Markdown deliverables, or slide-like artifacts. + +## Common Visual Failures + +- generic headings such as Overview, Key Points, Summary, or Next Steps when the user's domain needs sharper section names +- large citation or footnote clusters that break sentence flow +- Markdown tables with paragraph-length cells or weak hierarchy +- screenshots captured from the wrong state, viewport, crop, or missing asset +- HTML reports that look like raw JSON converted into cards +- repeated cards with identical weight, making the page hard to scan +- decorative gradients, shadows, or glass effects that do not serve the content +- mobile layouts that collapse into long undifferentiated blocks + +## Design Quality Gates + +### P0 Must Fix + +- no absolute `/Users/...` paths in final HTML +- no placeholder titles, labels, screenshots, or source notes +- no invented screenshots, charts, citations, or visual evidence +- no table with paragraph-length cells when bullets or cards would scan better +- no fixed design palette copied from another skill without content justification + +### P1 Should Fix + +- title and section headings use domain nouns and the target outcome +- each report has one clear first-screen explanation of what it is for +- visual hierarchy separates decisions, evidence, risks, and next actions +- dense content is split across sections instead of squeezed into one block +- reviewer-only detail is present but not pushed into the user's main reading flow + +### P2 Polish + +- typography roles are consistent +- whitespace rhythm supports reading speed +- cards, tables, and callouts are used for different semantic jobs +- source notes are grouped where they preserve flow +- mobile, print, and static-file viewing are considered when relevant + +## Self-Repair Pass + +Before handoff, scan the generated artifact for: + +1. heading specificity +2. table readability +3. citation density +4. screenshot truthfulness +5. local path leakage +6. placeholder remnants +7. mobile scanability +8. reviewer-visible evidence diff --git a/references/packaging-contracts.md b/references/packaging-contracts.md new file mode 100644 index 0000000..b734d76 --- /dev/null +++ b/references/packaging-contracts.md @@ -0,0 +1,70 @@ +# Packaging Contracts + +`cross_packager.py` is not just an export helper. It validates platform contracts and embeds target compiler output from `compile_skill.py`. + +## Current Targets + +- `openai` +- `claude` +- `generic` + +## Contract Shape + +Each target contract defines: + +- required output fields +- required output files +- field mapping from the neutral source metadata +- compiled contract from Skill IR +- target transform metadata, including generated files and unsupported features +- portable execution metadata +- trust-boundary metadata +- permission contract metadata from the trust report +- target-specific permission representation and reviewer notes +- target-native behavior contract for native surface, activation policy, resource strategy, script strategy, permission enforcement, install scope, review artifacts, and fallback behavior +- degradation strategy metadata + +## Failure Handling + +When `--expectations` is provided: + +- missing required files cause exit code `2` +- missing required fields cause exit code `2` +- validation failures are emitted in the JSON report + +After packaging, run `scripts/probe_runtime_permissions.py` against the generated package directory. Packaging creates the permission metadata; the runtime permission probe verifies that each target adapter exposes the contract, target-specific representation, native-enforcement flag, operator note, and residual metadata-fallback risk. + +## Source Of Truth + +The platform-neutral semantic source is Skill IR when it exists: + +- `reports/skill-ir.json` +- `skill-ir/examples/.json` + +The structural validation sources remain: + +- `SKILL.md` +- `agents/interface.yaml` + +Target-specific metadata is generated through `scripts/compile_skill.py` and +then embedded at packaging time. The adapter must carry `compiler`, +`compiled_contract`, `permission_contract`, `target_permission_contract`, +`target_native_contract`, `target_transform`, `ir_source`, `ir_schema_version`, +`job_to_be_done`, `semantic_contract`, and `semantic_parity` so reviewers can +see whether the target preserved the core skill meaning or fell back to +frontmatter-only metadata. + +## Portability Model + +The packaging layer now preserves four portable semantics from the neutral source: + +- activation +- execution +- trust +- permissions +- degradation +- platform-neutral skill meaning from Skill IR +- target-specific native behavior notes for activation, resources, scripts, permission enforcement, install scope, review artifacts, and fallback behavior +- target-specific compile notes for generated files, adapter mode, preserved semantics, and unsupported features + +This means portability is not just "can it export a file?" but also "does the exported target preserve the source package's activation and safety assumptions?" diff --git a/references/pattern-extraction-doctrine.md b/references/pattern-extraction-doctrine.md new file mode 100644 index 0000000..499901d --- /dev/null +++ b/references/pattern-extraction-doctrine.md @@ -0,0 +1,76 @@ +# Pattern Extraction Doctrine + +Use this doctrine when a skill borrows ideas from GitHub repositories, products, papers, experts, or user-supplied references. The goal is to extract durable patterns, not copy surface style. + +## Principle + +A borrowed pattern must improve the current skill's reliability, clarity, or portability faster than it increases context cost. + +## Four-Gate Pattern Test + +Accept a pattern only when it passes enough of these gates for the skill's risk tier. + +### 1. Recurrence + +The pattern appears in more than one serious example, source, workflow, or usage scenario. + +Use it to reject one-off tricks that look impressive but have no durable signal. + +### 2. Generativity + +The pattern can guide a new case, not just explain the original example. + +Use it to prefer operating principles, decision rules, and workflow loops over anecdotes. + +### 3. Distinctiveness + +The pattern is more specific than generic good advice. + +Use it to reject empty claims such as "be clear", "be useful", or "add quality" unless the reference shows how. + +### 4. Boundary + +The pattern has a known limit: when not to apply it, what not to borrow, or what cost it introduces. + +Use it to prevent reference scans from becoming unbounded feature expansion. + +## Acceptance Rule + +- `Scaffold`: accept a pattern when it has generativity and boundary clarity. +- `Production`: require recurrence, generativity, and boundary clarity. +- `Library`: require recurrence, generativity, distinctiveness, and boundary clarity. +- `Governed`: require all four gates plus reviewer-visible evidence. + +When the evidence is not strong enough, move the pattern into an iteration candidate instead of the first package. + +## What To Borrow + +Borrow: + +- high-signal workflow loops +- evidence-backed quality gates +- repeatable review checkpoints +- crisp output shapes +- boundary language that prevents route confusion +- portability patterns that preserve meaning across environments + +## What Not To Borrow + +Do not borrow: + +- source branding +- long prose +- roleplay style that does not match the target skill +- heavy research workflows for low-risk scaffolds +- platform-specific assumptions hidden inside a general method +- impressive examples that cannot be verified or generalized + +## Reviewer Questions + +Before accepting a borrowed pattern, ask: + +- Where else does this pattern appear? +- What new case can it help solve? +- What makes it more specific than generic advice? +- When should this skill refuse to use it? +- What file, report, eval, or checklist proves it earned its weight? diff --git a/references/platform-capability-matrix.md b/references/platform-capability-matrix.md new file mode 100644 index 0000000..57f5fa9 --- /dev/null +++ b/references/platform-capability-matrix.md @@ -0,0 +1,49 @@ +# Platform Capability Matrix + +This matrix describes the current packaging targets and their support level. + +| Target | Metadata Adapter | Compiler Contract | Native Behavior Contract | Output Contract | Snapshot Test | Portability Semantics | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `openai` | Yes | Yes | Yes | Yes | Yes | activation, execution, trust, permissions, degradation, native behavior | Generates `targets/openai/agents/openai.yaml` | +| `claude` | Yes | Yes | Yes | Yes | Yes | activation, execution, trust, permissions, degradation, native behavior | Generates `targets/claude/README.md` plus adapter metadata | +| `generic` | Yes | Yes | Yes | Yes | Yes | activation, execution, trust, permissions, degradation, native behavior | Uses neutral adapter metadata only | +| `agent-skills-compatible` | Neutral source | Yes | Yes | Source-compatible | Yes | activation, execution, trust, permissions, degradation, native behavior | Keeps canonical `SKILL.md` plus `agents/interface.yaml` source shape | +| `vscode` | Yes | Yes | Yes | Yes | Yes | activation, execution, trust, permissions, degradation, native behavior, install scope | Generates `targets/vscode/README.md` plus adapter metadata for VS Code / Copilot Agent Skills review | + +## Current Support Model + +- `openai`: strongest metadata adapter support with an explicit compiler contract. +- `claude`: lightweight compatibility adapter with an explicit compiler contract and fallback notes. +- `generic`: lowest-friction export for neutral Agent Skills consumers. +- `agent-skills-compatible`: canonical source shape with compiler evidence for review and distribution. +- `vscode`: VS Code / Copilot Agent Skills adapter that preserves the neutral source package and documents user/project scope plus workspace-trust review notes. +- runtime permission probes currently report metadata fallback for generated targets; no target is claimed as native-enforced until a client or installer integration can actually enforce the permission model. + +## Portable Semantics + +Each target now preserves: + +- activation mode and optional path filters +- execution context and shell choice +- trust tier and remote inline-execution policy +- permission contract for network, file-write, subprocess, and interactive script surfaces +- target-native behavior contract for native surface, activation policy, resource strategy, script strategy, permission enforcement, install scope, review artifacts, and fallback behavior +- degradation strategy for unsupported client behavior +- generated-file mapping and adapter mode from `reports/compiled_targets.json` + +## Explicit Non-Goals + +This project does not yet implement: + +- client SDK integration +- provider-specific execution logic +- provider-native installer actions or account-level activation changes +- native runtime permission enforcement + +## Degradation Rule + +If a target cannot support a source feature directly: + +1. preserve the neutral source package +2. emit a minimal adapter +3. document the fallback in the target output diff --git a/references/prompt-engineering-doctrine.md b/references/prompt-engineering-doctrine.md new file mode 100644 index 0000000..62ef4fb --- /dev/null +++ b/references/prompt-engineering-doctrine.md @@ -0,0 +1,76 @@ +# Prompt Engineering Doctrine + +Use this doctrine when a skill creates, improves, audits, or relies on prompts, role instructions, conversation scripts, writing systems, teaching guides, analysis instructions, or reusable task templates. + +## Principle + +Prompt quality is a skill-design input, not a long prompt to paste into `SKILL.md`. + +The useful abstraction is not a fixed RTF template. The useful abstraction is a compact reasoning layer: + +- understand the real need behind the request +- choose the right task type and complexity +- map role, task, and format into skill structure +- score the prompt-facing behavior before the skill is treated as reusable + +## Need Model + +Before writing a prompt-heavy skill, identify: + +- explicit need: what the user clearly asked for +- implicit need: what the context suggests but the user did not name +- scenario: where and how the output will be used +- user level: beginner, practitioner, expert, reviewer, or operator +- success standard: what proves the output worked + +If any of these change the package boundary, ask one focused clarification. If they only affect implementation detail, record the assumption in a report instead of interrupting the user. + +## Task Families + +- creative generation: content, ideas, campaigns, variants, concepts +- analytical reasoning: diagnosis, comparison, synthesis, decision support +- execution operation: workflow steps, task completion, standardized operations +- teaching guidance: explanation, curriculum, walkthrough, coaching +- dialogue interaction: support, interview, roleplay, discovery, coaching +- prompt engineering: prompt creation, prompt improvement, prompt review, prompt libraries + +## Complexity + +- simple: one output, few constraints, low ambiguity +- medium: multiple steps, some judgment, moderate standards +- complex: multiple inputs, tradeoffs, high-quality output expectations +- expert: domain expertise, evaluation, governance, or safety-sensitive use + +Complexity should control how much structure is added. It should not justify bloating the entrypoint. + +## RTF To Skill Mapping + +| Prompt Layer | Skill Layer | Reviewer Question | +| --- | --- | --- | +| Role | operating stance, expertise, tone | Does the agent identity match the job and user level? | +| Task | workflow, gates, scripts, references | Are the steps executable and verifiable? | +| Format | output contract, examples, reports | Is the hand-back useful, readable, and testable? | + +## Quality Matrix + +Score prompt-facing behavior on: + +- completeness: enough context, constraints, and outputs are specified +- clarity: wording is unambiguous and easy to execute +- consistency: role, task, format, examples, and boundaries agree +- practicality: the output can be used without hidden assumptions +- specificity: language fits the user's domain instead of generic prompt jargon + +Treat innovation as optional. A reusable skill should first be clear, reliable, and specific. + +## Anti-Patterns + +- copying a full meta-prompt into `SKILL.md` +- adding an elaborate persona when the workflow only needs a narrow capability +- asking the user for every possible field instead of the few fields that change design +- producing a polished prompt that lacks tests, examples, or output checks +- using RTF labels as decoration without tying them to skill behavior + +## Reviewer Rule + +For prompt-heavy skills, reviewers should see the need model, task family, complexity, RTF-to-skill mapping, and quality matrix. If those are absent, the package may still run but its prompt behavior is not governed. diff --git a/references/qa-ladder.md b/references/qa-ladder.md new file mode 100644 index 0000000..e5cc1f6 --- /dev/null +++ b/references/qa-ladder.md @@ -0,0 +1,57 @@ +# QA Ladder + +Use the smallest quality gate set that still protects the user from likely failure. + +## Basic + +Use when: + +- the skill is disposable or exploratory +- the route is obvious +- there is little downside to imperfect output + +Recommended checks: + +- structure sanity +- naming alignment +- a quick read for boundary clarity + +## Standard + +Use when: + +- the skill will be reused +- near-neighbor prompts are plausible +- references or scripts could drift from the main instructions + +Recommended checks: + +- `validate_skill.py` +- `resource_boundary_check.py` +- a small trigger prompt set +- one description optimization pass when route wording is still unstable +- one realistic output example + +## Advanced + +Use when: + +- the skill is shared infrastructure +- packaging or routing errors would be costly +- you want evidence that the skill stays healthy over time + +Recommended checks: + +- description optimization suite with dev and holdout cases +- family-based trigger regression +- failure and anti-pattern regression +- governance scoring +- packaging contract validation +- regression history and result reporting + +## Escalation Heuristics + +- add trigger eval before writing more instruction detail +- add boundary checks before adding more folders +- add governance and history once the skill becomes a maintained asset +- do not add advanced checks only for optics diff --git a/references/reference-scan.md b/references/reference-scan.md new file mode 100644 index 0000000..0ea9ecb --- /dev/null +++ b/references/reference-scan.md @@ -0,0 +1,126 @@ +# Reference Scan Strategy + +Use a short benchmark scan before deep authoring. The goal is to borrow durable patterns from strong reference objects without importing their prose, weight, or brand language into the new skill. + +## Source Priority + +Reference scan has two layers, and they must not be treated equally: + +1. **External Benchmark Scan** + - primary source of patterns + - use high-star public GitHub repos, official docs, strong public examples, and world-class products + - this layer defines the upper bound for quality +2. **User Reference Intake** + - a taste-and-standard layer + - ask whether the user has reference repos, products, pages, prompts, or systems they admire + - learn only the pattern, structure, boundary, or quality bar; never copy wording or private material +3. **Local Fit Check** + - secondary calibration layer + - use local files only for naming, privacy, compatibility, migration, and library-fit constraints + - this layer should not define the main design pattern + +External sources should lead. User references should sharpen direction. Local files should calibrate. + +## Default Visibility + +Reference synthesis should be silent by default. + +- do the benchmark scan and pattern synthesis in the background +- convert the result into a recommendation for the user +- surface the full evidence only to authors and reviewers +- only expose the underlying tradeoffs to the user when intent is still uncertain or a real design conflict needs a decision + +## Why This Step Exists + +A new skill often fails because it starts from an isolated idea instead of a proven pattern. A controlled reference scan improves the package before it grows: + +- better boundary design +- cleaner folder and metadata choices +- more realistic quality gates +- stronger portability decisions +- better alignment with the user's own taste and quality bar + +## The Rule + +Reference scanning is mandatory for: + +- `Production` skills +- `Library` skills +- `Governed` skills +- meta-skills or packaging-heavy skills + +Reference scanning is optional for: + +- `Scaffold` skills +- one-person exploratory skills + +## Scope Limit + +Do not turn this into open-ended research. + +- scan at most `3-5` reference objects +- pick from no more than `3` categories +- extract patterns, not long copied content +- stop as soon as the borrow plan is clear +- prefer at least `2` external benchmark objects before treating the scan as complete +- if the user provides references, record what they admire and what should explicitly not be copied + +## Pattern Acceptance + +Borrowed ideas must pass a lightweight pattern test before they shape the first package. + +- `recurrence`: the pattern appears in more than one serious context or source +- `generativity`: the pattern can guide new cases, not just explain one example +- `distinctiveness`: the pattern is more specific than generic good advice +- `boundary`: the pattern names where not to apply it or what cost it introduces + +For low-risk scaffold work, generativity plus boundary clarity can be enough. For production, library, and governed work, require more evidence before the pattern changes the package shape. + +See [Pattern Extraction Doctrine](pattern-extraction-doctrine.md). + +## Reference Categories + +Choose the smallest relevant set: + +- `method`: loops, evaluation discipline, iteration structure +- `structure`: package anatomy, resource boundaries, metadata patterns +- `execution`: operator flow, scripts, initialization and validation experience +- `portability`: neutral metadata, adapters, degradation strategy +- `domain`: workflow-specific patterns from a top example in the same problem space + +## Output Format + +A good scan produces a short report with: + +1. current skill anchor +2. scan focus +3. external benchmark objects +4. user-supplied references +5. local fit constraints +6. what to borrow +7. what not to borrow +8. a compact borrow plan + +## What To Borrow + +Borrow: + +- repeatable loops +- clear boundary patterns +- proven gate choices +- portable metadata ideas +- clear operator-facing flows +- high-signal examples that show the finished experience, not just the internal method + +Do not borrow: + +- source-specific branding +- long copied prose +- unnecessary directories +- quality gates that exceed the skill's risk tier +- platform lock-in disguised as best practice +- local historical habits that are weaker than public top-tier benchmarks + +## Design Principle + +The scan is successful only if it raises skill quality faster than it raises context cost. If benchmark material makes the new skill heavier without making it more reliable, discard it. diff --git a/references/regression-cause-taxonomy.md b/references/regression-cause-taxonomy.md new file mode 100644 index 0000000..df5e2ce --- /dev/null +++ b/references/regression-cause-taxonomy.md @@ -0,0 +1,80 @@ +# Regression Cause Taxonomy + +This taxonomy explains how iteration regressions are classified when a description candidate is evaluated for promotion. + +## Core Principle + +A candidate should not be judged only by aggregate pass/fail counts. The iteration system should explain why a candidate was blocked, kept behind the current description, or promoted. + +## Cause Tags + +### `no_candidate_outperformed_current` + +The selected winner is still the current description. No candidate earned promotion. + +### `visible_holdout_regression` + +The candidate regressed on the visible holdout suite by adding false positives or false negatives. + +### `blind_holdout_regression` + +The candidate regressed on blind holdout prompts. This blocks promotion because the failure is not only local to the tuning loop. + +### `current_holdout_gap_present` + +The current or selected winner still carries a visible holdout miss. Promotion may still stay on `keep_current`, but the iteration bundle should show the unresolved gap. + +### `current_holdout_risk` + +The visible holdout calibration still looks risky even when promotion is not blocked. This is an audit signal that the route boundary needs future work. + +### `judge_blind_regression` + +The rubric judge found worse blind-holdout behavior than the current or baseline description. + +### `judge_blind_low_agreement` + +The judge-backed blind evaluation did not produce enough agreement confidence to support promotion. + +### `adversarial_regression` + +The candidate performed worse on adversarial holdout prompts that simulate route collisions or disguised requests. + +### `adversarial_overlap_risk` + +The adversarial calibration layer reports an `overlap` risk band, meaning route boundaries are too weak for safe promotion. + +### `adversarial_watch_risk` + +The adversarial calibration layer reports a non-failing but cautionary risk band such as `watch` or `tight`. + +### `family_instability` + +At least one tracked family stops being clean under blind, judge-backed blind, or adversarial evaluation. + +### `route_confusion` + +The route confusion matrix shows route theft or misrouting between sibling skills. + +### `route_ambiguity` + +The route confusion matrix reports ambiguous cases near the configured margin-warning threshold. + +### `longer_without_gain` + +The candidate is materially longer than the current description without producing a better route outcome. + +### `promotion_ready` + +The candidate passed every promotion gate and is eligible for review and promotion. + +## Usage + +These cause tags should appear in: + +- promotion decisions +- iteration bundles +- regression histories +- human review summaries + +They are intended to make iteration auditable rather than merely descriptive. diff --git a/references/resource-boundaries.md b/references/resource-boundaries.md new file mode 100644 index 0000000..a6f88bf --- /dev/null +++ b/references/resource-boundaries.md @@ -0,0 +1,120 @@ +# Resource Boundary Spec + +This spec defines where information belongs inside a skill package. + +## Principle + +Keep the main skill small enough to route and execute clearly. Move detail out of `SKILL.md` as soon as it stops helping routing or branch selection. + +Do not add structure for imagined future needs. A folder, script, eval, or governance file belongs in the package only when it reduces current ambiguity, execution burden, route risk, or maintenance risk. + +## Context Budget Tiers + +Use the lightest budget that still fits the package. + +- `scaffold`: `700` initial-load tokens +- `production`: `1000` initial-load tokens +- `library`: `1300` initial-load tokens +- `governed`: `1300` initial-load tokens + +If `manifest.json` sets `context_budget_tier`, that tier overrides the default budget derived from lifecycle or maturity metadata. This allows a high-governance skill to keep a stricter initial-load budget than its lifecycle label alone would imply. + +## Placement Rules + +### Put content in `SKILL.md` when it is: + +- part of the trigger surface +- part of the core execution skeleton +- part of the output contract +- necessary for branch selection or safe defaults + +### Put content in `references/` when it is: + +- domain guidance +- long examples +- policy material +- schemas or templates humans or agents may read on demand + +### Put content in `scripts/` when it is: + +- deterministic +- repetitive +- brittle if rewritten from prose +- easier to validate as code than as instructions + +### Put content in `evals/` when: + +- the skill is reused enough that routing mistakes matter +- near-neighbor confusion is likely +- quality claims should be reproducible + +### Put content in `assets/` when: + +- the package includes output artifacts, examples, or static files that should not bloat prompt context + +## Anti-Patterns + +Avoid these: + +- storing long policy text directly in `SKILL.md` +- adding `references/` with no files that are actually used +- adding `scripts/` for logic that is still best expressed in prose +- adding `evals/` for one-off or disposable skills +- creating every folder by default even when empty +- keeping folders that are neither referenced in `SKILL.md` nor declared as factory components +- adding broad configuration knobs before a real variation exists +- adding governance or reports to make a scaffold look mature when no reuse pressure exists + +## Heuristics + +### `SKILL.md` + +- should stay focused +- should not become the full knowledge base +- should mention any optional directory that materially affects execution + +### `references/` + +- should earn their keep +- should usually be named and discoverable from `SKILL.md` + +### `scripts/` + +- should exist only when deterministic logic or formatting logic is real +- should be referenced explicitly from `SKILL.md` when required for execution + +### `evals/` + +- should exist when routing or quality claims need to be defended +- should be skipped for disposable personal drafts + +## Unused Resource Detection + +`resource_boundary_check.py` warns when a non-empty optional directory appears decorative: + +- the directory exists and contains files +- the main `SKILL.md` does not reference it +- and the directory is not declared in `manifest.json` factory components + +This protects the package from looking more sophisticated than it actually is. + +## Quality Density + +The checker also reports `quality_density`, a local signal for how much governance and quality evidence is packed into the initial load budget. + +It combines: + +- governance score +- presence of evals +- presence of reports +- references and scripts +- interface and manifest metadata +- failure or test evidence + +Higher density means the package is staying lean while still proving quality. + +## Quality Intent + +The best skill is not the one with the most files. The best skill is the smallest package that still makes the recurring job reliable, reusable, and auditable. + +See [Authoring Discipline](authoring-discipline.md) for the author and reviewer rules that keep resource growth tied to a real user goal. diff --git a/references/review-studio-method.md b/references/review-studio-method.md new file mode 100644 index 0000000..4674fbd --- /dev/null +++ b/references/review-studio-method.md @@ -0,0 +1,87 @@ +# Review Studio 2.0 Method + +Review Studio is the release-facing audit surface for a skill package. It does not replace the detailed reports; it turns them into one reviewer decision page. + +## Purpose + +- Show release blockers and warnings before the package deepens. +- Link every gate back to a concrete evidence artifact. +- Generate review actions for every blocker and warning, with source-fix location and verification command. +- Make human warning acceptance auditable through a waiver ledger. +- Make reviewer comments auditable through an annotation ledger tied to gates, source/report paths, and optional line numbers. +- Keep review flow vertical: summary first, gates second, supporting details after. +- Avoid hiding output quality, runtime, trust, portfolio, and operating-loop issues across separate pages. + +## Required Gates + +1. Intent Canvas: intent confidence and unresolved input/output/exclusion gaps. +2. Trigger Lab: route scorecard, misroutes, ambiguous cases, and near-neighbor safety. +3. Output Lab: with-skill vs baseline delta, execution mode, timing/token evidence, case count, file-backed cases, near-neighbor cases, boundary cases, blind A/B review pack evidence, and reviewer adjudication status. +4. Context Budget: initial load, budget tier, warnings, and quality density. +5. Runtime Matrix: target conformance pass/fail and degradation notes. +6. Trust Report: secret scan, script surface, dependency pinning, network/interactive flags, and package hash. +7. Permission Gates: reviewer-approved capability scope, reason, expiry, evidence, and target-enforcement notes. +8. Runtime Permission Probes: packaged adapter permission contracts, native-enforcement flags, metadata fallback notes, and residual risks. +9. Skill Atlas: route collisions, stale skills, owner gaps, and no-route opportunities. +10. Operations Loop: local-first metadata-only adoption, missed-trigger, bad-output, script-error, and review-drift signals. +11. Review Waivers: human risk approvals, active coverage, expired approvals, invalid records, and expiry policy. +12. Registry Audit: package metadata, install evidence, compatibility entries, and archive/source checksums. +13. Release Notes: promotion status, migration notes, known gaps, and next move. + +## Gate Semantics + +- `pass`: evidence is present and the gate is satisfied. +- `warn`: review can continue, but the issue must be visible before release. +- `block`: do not claim production, library, governed, or public readiness until fixed. + +For library and governed skills, Output Lab should have at least five cases and cover file-backed, near-neighbor, and boundary scenarios. + +Production, library, and governed reviews should also show a blind A/B review pack. The Review Studio gate may warn when scorecard evidence exists but no blind pack is present, because the package can prove assertions but not yet reduce reviewer bias. + +When `reports/output_execution_runs.json` exists, Review Studio should show the number of variant runs, command-executed runs, model-executed runs, recorded fixtures, timing-observed runs, and token-estimated runs. Recorded fixtures are valid reproducibility evidence, but they must not be described as model-executed output evidence. + +When `reports/output_review_adjudication.json` exists, Review Studio should show reviewed pairs and pending pairs. Pending reviewer decisions are acceptable as an explicit state, but they must not be counted as agreement or human review evidence. For production, library, and governed packages, pending reviewer decisions should keep the Output Lab in `warn` until reviewer decisions are recorded or the warning is explicitly accepted in the waiver ledger. Invalid adjudication records should block release because they make the blind review audit untrustworthy. + +The Operations Loop must never display raw telemetry logs. It should link only to `reports/adoption_drift_report.md`; privacy or schema violations are blockers. + +The Runtime Permission Probes gate is evaluated after packaging, because it reads generated target adapters. A missing probe can warn in lighter modes, but governed release should not claim target permission readiness without `reports/runtime_permission_probes.json`. + +The Review Waivers gate must never convert a blocker into a pass. Waivers only cover warning-level risks, require reviewer, reason, scope, and expiry, and must link only to `reports/review_waivers.md`. + +Review Annotations are not waivers. They are reviewer comments attached to a Review Studio gate plus a relative source/report path and optional line number. Use them to preserve review context, requested edits, and source-line notes. Open blocker annotations should make the Review Studio decision `blocked` until the annotation is resolved or deferred with reviewer rationale. Open warning annotations can move the package into review, but they do not create gate-specific `review_actions`; actions remain reserved for non-pass gates. + +## Review Actions + +Every non-pass gate must produce a `review_actions` entry in `reports/review-studio.json`. When all gates pass, `review_actions` should be an empty list and the page should explicitly say there are no blocker or warning actions. + +Each action must include: + +- `gate_key` +- `status` +- `summary` +- `why` +- `source_fix` +- `source_refs` +- `evidence` +- `verification_command` + +`source_refs` must be structured entries with relative `path`, human label, kind, existence flag, best-effort line number, matched pattern, short source excerpt, and relative link when the file exists. They should point to the smallest useful report or source file, not just a broad directory. The HTML page should render the excerpt next to the link so reviewers can understand why a line anchor matters before opening the full artifact. + +The HTML page should render these actions before the detailed supporting sections so a reviewer can move directly from warning to fix. Action entries do not change gate count or score; they make the current decision more operational. + +For `world-class-evidence`, the action should also expose an evidence-step card for every pending evidence key. Each card should show the submission path, template path, blocked source checks, command handoff, first runbook steps, provenance requirements, success checks, evidence artifacts, and privacy boundary. These cards are collection guidance only; they must not count as accepted evidence or change world-class readiness. + +## Review Annotations + +`reports/review_annotations.json` is the structured ledger, and `reports/review_annotations.md` is the human-readable review note surface. Each annotation should include: + +- `gate_key` +- `target_path` +- `line` when a useful source line exists +- `severity` +- `status` +- `reviewer` +- `body` +- optional `suggested_action` + +The ledger should reject absolute paths or paths that escape the skill directory. Missing target files are allowed as visible evidence gaps, not hidden failures. diff --git a/references/review-waiver-method.md b/references/review-waiver-method.md new file mode 100644 index 0000000..84d3fc7 --- /dev/null +++ b/references/review-waiver-method.md @@ -0,0 +1,76 @@ +# Review Waiver Method + +Review waivers make human risk acceptance explicit. They are not a way to hide problems; they are a local audit record for warning-level risks that the reviewer intentionally accepts for a bounded release window. + +## When To Use + +Use a waiver when: + +- Review Studio shows a warning that is understood and intentionally accepted. +- The warning cannot be fixed before release without a worse tradeoff. +- A reviewer can name the reason, scope, evidence, and expiry date. + +Do not use a waiver for blocker gates in v0. Blockers must be fixed before production, library, governed, or public readiness is claimed. In governed mode, missing or invalid high-permission approvals are blockers and should be fixed in `security/permission_policy.json`, not waived. + +## Required Fields + +Every waiver must include: + +- `gate_key`: the Review Studio gate being accepted. +- `decision`: `accepted-risk`, `false-positive`, or `temporary-exception`. +- `reviewer`: the accountable human or team. +- `reason`: a concrete reason of at least 20 characters. +- `created_at`: ISO date. +- `expires_at`: ISO date. +- `evidence`: optional path or note that explains the decision. +- `scope`: default `current-release`. + +## Gate Key Policy + +The waiver ledger must track the Review Studio gate universe explicitly: + +- `review_studio_gate_keys`: every gate Review Studio can render. +- `waiverable_gate_keys`: warning gates that may receive bounded human acceptance. +- `non_waivable_gate_keys`: gates that must not be accepted through a waiver. + +When Review Studio adds or renames a gate, update the waiver gate policy and tests in the same change. `review-waivers` and `world-class-evidence` stay non-waivable: the first is the waiver mechanism itself, and the second can only be satisfied by accepted ledger evidence. + +## Release Semantics + +- Invalid waiver records block Review Studio. +- Expired waiver records stay visible and no longer cover warnings. +- Active waivers cover only the exact gate key they name. +- A warning without an active waiver remains visible as a warning. +- Raw user prompts, outputs, credentials, and private transcripts must not be stored in waiver reasons. + +## Commands + +Render or validate the ledger: + +```bash +python3 scripts/render_review_waivers.py . +``` + +Add a bounded approval: + +```bash +python3 scripts/yao.py review-waivers . \ + --add-waiver \ + --gate-key trust-report \ + --reviewer "Yao Team" \ + --reason "Network-capable scripts are documented and bounded for this release." \ + --expires-at 2026-09-30 +``` + +For a non-governed release where `permission-gates` is only a warning, the same command can name `--gate-key permission-gates`. Governed releases must instead provide reviewer, scope, reason, expiry, evidence, and target-enforcement fields in `security/permission_policy.json`. + +Review Studio reads `reports/review_waivers.json` and links to `reports/review_waivers.md`. + +## Candidate Actions + +The waiver report also surfaces current candidate actions from local evidence: + +- waiverable warning candidates, such as an `output-lab` warning caused by pending reviewer decisions or missing provider-backed runs +- non-waivable boundaries, especially `world-class-evidence`, where pending ledger evidence cannot be converted into completion by a waiver + +A waiver can make a bounded warning auditable for a release window. It cannot count as provider-backed evidence, human adjudication, native runtime enforcement, external telemetry, or public world-class readiness. diff --git a/references/runtime-conformance-method.md b/references/runtime-conformance-method.md new file mode 100644 index 0000000..8bef7fd --- /dev/null +++ b/references/runtime-conformance-method.md @@ -0,0 +1,21 @@ +# Runtime Conformance Method + +Runtime conformance turns platform compatibility from a packaging afterthought into a release gate. + +## Purpose + +Use this check when a skill is packaged for OpenAI, Claude, Agent Skills, VS Code / Copilot, or generic targets. The goal is not to prove that every runtime behaves identically. The goal is to prove that the package exposes enough metadata, files, and degradation notes for each runtime to consume it safely. + +## V0 Checks + +- `SKILL.md` exists and has frontmatter `name` and `description`. +- `description` stays within the 1024 character limit used by common Agent Skills clients. +- `manifest.json` includes name, version, owner, maturity, status, review cadence, and target platforms. +- `agents/interface.yaml` includes display text, default prompt, activation mode, execution context, trust metadata, adapter targets, and degradation strategy. +- Skill IR exists and matches the frontmatter name and description. +- Resources named by Skill IR are relative paths and resolve inside the package. +- Unsupported or lossy target behavior is represented by a degradation note. + +## Reviewer Gate + +A reviewer should be able to see a target matrix with pass/fail status, failures, warnings, and artifact paths. Any failed target blocks library, governed, or team-distributed release for that target. diff --git a/references/skill-archetypes.md b/references/skill-archetypes.md new file mode 100644 index 0000000..cd8e6f5 --- /dev/null +++ b/references/skill-archetypes.md @@ -0,0 +1,86 @@ +# Skill Archetypes + +Use these archetypes to decide what kind of skill you are building before you decide how many files or gates to add. + +## Scaffold + +Purpose: + +- quick packaging for a real but still exploratory workflow + +Default assets: + +- `SKILL.md` +- `agents/interface.yaml` + +Use when: + +- reuse is plausible but not proven +- failure cost is low +- the workflow is still changing + +## Production + +Purpose: + +- compact skill for team reuse + +Default assets: + +- lean `SKILL.md` +- `agents/interface.yaml` +- selective `references/` +- selective `evals/` + +Use when: + +- route mistakes waste team time +- a checklist or focused script improves reliability + +## Library + +Purpose: + +- shared capability with visible evidence and portability expectations + +Default assets: + +- route evals +- packaging checks +- manifest metadata +- public reports + +Use when: + +- the skill will be reused across teams or clients +- the skill is likely to have near-neighbor route collisions + +## Governed + +Purpose: + +- high-trust skill with explicit ownership and review + +Default assets: + +- lifecycle metadata +- governance score +- review cadence +- regression history +- governed examples or policy references + +Use when: + +- the skill is operationally sensitive +- the skill influences incident, release, compliance, or organizational standards + +## Anti-Archetypes + +Do not force a request into a skill archetype when it is really: + +- a one-off answer +- a document +- a brainstorm +- an implementation task with no reusable process + +See [Non-Skill Decision Tree](non-skill-decision-tree.md). diff --git a/references/skill-atlas-method.md b/references/skill-atlas-method.md new file mode 100644 index 0000000..9c58043 --- /dev/null +++ b/references/skill-atlas-method.md @@ -0,0 +1,35 @@ +# Skill Atlas Method + +Skill Atlas is the 2.0 operating layer for a workspace that contains many skills. + +## Purpose + +Single-skill quality is not enough for a team library. A skill portfolio also needs to reveal route collisions, stale ownership, duplicate resources, and repeated no-route opportunities. + +## V0 Checks + +- Catalog every `SKILL.md` under a workspace. +- Extract name, description, owner, maturity, targets, updated date, and review cadence. +- Detect similar descriptions as route-overlap candidates. +- Detect duplicate skill names. +- Detect shared script/reference filenames as dependency signals. +- Flag missing owner or review metadata. +- Flag stale skills based on `updated_at` and `review_cadence`. +- Extract no-route opportunities from failure notes. +- Read aggregate adoption drift reports and flag telemetry drift without reading raw telemetry logs. + +## Scope Policy + +Atlas keeps a full catalog, but release gates should distinguish actionable library skills from examples and test fixtures. + +Use `skill_atlas/policy.json` to mark path prefixes as non-actionable when they are intentionally retained as examples, evolution snapshots, embedded generated skills, or validator fixtures. Non-actionable items still appear in the full report, route matrix, stale list, and owner gap list, but Review Studio should use the actionable counts for release readiness. + +## Telemetry Link + +Atlas may read each skill's aggregate `reports/adoption_drift_report.json` to surface portfolio drift signals such as no telemetry for production/library/governed skills, missed triggers, bad outputs, missing resources, script errors, and review-overdue counts. It must not read or package `reports/telemetry_events.jsonl`; raw telemetry remains local-only evidence owned by the skill. + +Write drift output to `skill_atlas/drift_signals.json`. Non-actionable scopes stay visible in that file and in the HTML report, but only actionable drift signals should affect release readiness. + +## Reviewer Gate + +Use Atlas before promoting a single skill into a shared library. If an actionable route collision, missing owner, stale governed skill, or telemetry drift signal appears, fix the portfolio boundary before adding more local complexity to one skill. Non-actionable issues should stay visible as evidence, not as release blockers. diff --git a/references/skill-engineering-method.md b/references/skill-engineering-method.md new file mode 100644 index 0000000..10cd3d1 --- /dev/null +++ b/references/skill-engineering-method.md @@ -0,0 +1,210 @@ +# Skill Engineering Method + +This doctrine defines the default method for turning messy workflow material into a reusable skill without bloating the entrypoint. + +## Core Loop + +1. Decide whether the request should become a skill at all. +2. Run a short intent dialogue to capture the real job, outputs, exclusions, and constraints. +3. Choose the smallest viable archetype. +4. Set one clear capability boundary. +5. Write and test the trigger description before expanding the body. +6. Apply authoring discipline: name unresolved assumptions, keep scope small, and tie meaningful changes to checks. +7. Add an output risk profile for user-facing artifacts. +8. Add an artifact design profile for reports, tutorials, viewers, dashboards, screenshots, and visual pages. +9. Add only the gates that match the risk. +10. Ship the first routeable package, then pick the three highest-value next iteration directions. +11. Package and govern the skill only as far as real reuse demands. + +## Phase 1: Qualification + +Promote a request into a skill only when at least one of these is true: + +- the workflow will be reused +- the workflow is easy to route incorrectly +- deterministic scripts reduce repeated effort +- governance or portability matters + +Reject skill creation when the request is only: + +- explanation +- summary +- translation +- brainstorming +- documentation without agent execution +- a one-off answer with no reuse value + +See [Non-Skill Decision Tree](non-skill-decision-tree.md). + +## Phase 1.5: Authoring Discipline + +Before expanding the package, apply the execution discipline that keeps the work grounded. + +- clarify only the assumptions that change the package design +- do not add speculative features, generic configurability, or decorative structure +- when editing an existing skill, touch only files that directly serve the requested change +- connect each meaningful change to a check: route evidence, sample run, resource-boundary check, governance check, or reviewer note + +See [Authoring Discipline](authoring-discipline.md). + +## Phase 1.6: Problem Diagnosis + +When the user gives a fuzzy pain point instead of a clear skill request, diagnose the likely package shape before asking for structure. + +- infer whether the need is best served by a scaffold, production workflow, library capability, governed asset, or no skill +- recommend at most three candidate directions +- explain why each candidate fits, where it is limited, and what the first light version should prove +- prefer a concrete recommendation over a menu when the intent is clear enough + +This keeps discovery useful for users who can describe the problem but not the skill architecture. + +## Phase 2: Intent Dialogue + +Before deep authoring, ask only the questions that change the package design. + +- open with a human, teacher-like framing rather than a cold field list +- let the user answer naturally first; offer a tiny template only as an optional shortcut +- what recurring job should the skill own +- what real inputs will users hand to it +- what outputs must it produce +- what near-neighbor requests should stay out of scope +- whether the user has reference systems, repos, or products worth learning from +- what constraints matter: privacy, naming, portability, governance, or local fit + +See [Intent Dialogue](intent-dialogue.md). + +## Phase 3: Archetype Selection + +Choose the lightest archetype that fits the job. + +- `Scaffold`: exploratory, personal, or short-lived +- `Production`: team-reused, quality-sensitive, but still compact +- `Library`: broad reuse, visible evidence, portability, and maintenance expectations +- `Governed`: organizationally sensitive or operationally critical; lifecycle and review are explicit + +See [Skill Archetypes](skill-archetypes.md). + +## Phase 4: Boundary Design + +Every skill should answer four questions clearly: + +- what recurring job does it own +- what outputs does it produce +- what near-neighbor requests should not route here +- what detail belongs outside `SKILL.md` + +Boundary work comes before polishing prose. + +## Phase 5: Reference Scan + +Run a short benchmark pass before deep authoring. + +- scan `3-5` reference objects at most +- prioritize high-star external GitHub and official benchmark sources first +- ask for user-supplied references second, but extract only patterns and standards +- use local files third, only for fit, privacy, and compatibility calibration +- choose from method, structure, execution, portability, and domain patterns +- extract only what passes the pattern test: recurrence, generativity, distinctiveness, and boundary clarity +- record what not to borrow so the new skill stays light + +See [Reference Scan Strategy](reference-scan.md) and [Pattern Extraction Doctrine](pattern-extraction-doctrine.md). + +## Phase 5.5: Output Risk Profiling + +Before treating the package as usable, predict the likely mistakes in its final user-facing output. + +- tutorial skills should guard against generic headings, vague examples, and missing success checks +- report and Markdown skills should guard against weak tables, dense lists, and poor hierarchy +- screenshot or visual skills should guard against wrong captures, missing assets, and invented visual evidence +- research or citation-heavy skills should guard against footnote clutter and unsupported claims +- code or command skills should guard against hidden cwd, input, output, and side-effect assumptions + +Generate `reports/output-risk-profile.md` and expose it to the reviewer before adding more structure. + +See [Output Quality Risk](output-quality-risk.md). + +## Phase 5.6: Artifact Design Profiling + +Before approving generated reports or visual outputs, define how the artifact should read and scan. + +- choose the artifact family: tutorial, report, review viewer, dashboard, visual evidence, slide-like narrative, or code/CLI guide +- let the content choose the visual system instead of copying a fixed house style +- borrow document discipline from Kami: route by document type, distill content, verify layout-critical assumptions +- borrow slide discipline from presentation skills: plan hierarchy, density, rhythm, and quality gates before writing HTML +- reject generic headings, noisy citations, weak tables, wrong screenshots, repeated card grids, and decorative visual defaults + +Generate `reports/artifact-design-profile.md` and expose it in the overview and review viewer. + +See [Artifact Design Doctrine](artifact-design-doctrine.md) and [Output Visual Quality](output-visual-quality.md). + +## Phase 5.7: Prompt Quality Profiling + +When a skill depends on prompt behavior, role design, dialogue quality, or output contracts, turn prompt methodology into an evidence profile. + +- identify the explicit need, implicit need, scenario, user level, and success standard +- map Role, Task, and Format into skill behavior instead of copying a full meta-prompt into `SKILL.md` +- classify the task family and complexity before adding structure +- score completeness, clarity, consistency, practicality, and specificity +- expose the full reasoning to reviewers while keeping the user-facing flow recommendation-led + +Generate `reports/prompt-quality-profile.md` and expose it in the overview and review viewer. + +See [Prompt Engineering Doctrine](prompt-engineering-doctrine.md). + +## Phase 6: Trigger-First Authoring + +Author the frontmatter `description` before expanding the body. + +- start with the recurring job +- include the trigger actions that should route here +- include exclusions when confusion is plausible +- test the route before growing the file tree + +Trigger quality is improved through: + +- `trigger_eval.py` +- `optimize_description.py` +- blind holdout +- judge-backed blind holdout +- adversarial holdout +- route confusion + +## Phase 7: Gate Selection + +Add gates by risk, not by habit. + +- low-risk scaffolds: validate structure and context size +- production skills: trigger eval plus resource-boundary checks +- library skills: description optimization, route confusion, packaging checks +- governed skills: governance scoring, lifecycle metadata, regression history + +See [Gate Selection](gate-selection.md). + +## Phase 8: First Iteration Philosophy + +The first package is a routeable baseline, not the final answer. + +- improve trigger and exclusions before growing prose +- add one execution asset before adding many documents +- surface the three highest-value next moves so authors do not expand in every direction at once +- prefer the smallest step that increases reliability more than context cost +- move unverifiable ideas into next-step candidates instead of shipping them as baseline structure + +See [Iteration Philosophy](iteration-philosophy.md) and [Authoring Discipline](authoring-discipline.md). + +## Phase 9: Promotion + +A candidate route or package is promotable only when: + +- visible holdout does not regress +- blind holdout does not regress +- judge-backed blind holdout does not regress +- adversarial holdout does not regress +- route confusion stays clean +- context and governance gates still pass + +See [Promotion Policy](../evals/promotion_policy.md). + +## Design Principle + +The method is only correct if rigor grows faster than context cost. If a new check or document makes the skill heavier without making it more reliable, remove or relocate it. diff --git a/references/skill-ir-method.md b/references/skill-ir-method.md new file mode 100644 index 0000000..574c83e --- /dev/null +++ b/references/skill-ir-method.md @@ -0,0 +1,41 @@ +# Skill IR Method + +Skill IR is the 2.0 layer that separates durable skill meaning from platform packaging. + +## Purpose + +Use Skill IR before platform-specific packaging for production, library, governed, or team-distributed skills. The IR should preserve the capability contract even when OpenAI, Claude, Agent Skills, VS Code, or generic adapters differ in folder layout, metadata names, or activation behavior. + +## What Belongs In IR + +- the recurring job the skill owns +- the frontmatter trigger description +- should-trigger, should-not-trigger, and near-neighbor edge cases +- workflow steps, decision points, and failure modes +- references, scripts, assets, and reports used by the package +- trigger and output eval plans +- output risk, execution risk, and trust boundary +- owner, maturity, review cadence, and target platforms + +## What Does Not Belong In IR + +- platform-specific file paths that only exist after packaging +- copied prose from external benchmarks +- client-specific UI labels +- local private paths that are not part of the skill package +- speculative adapters that are not requested or tested + +## Authoring Rule + +Export or update Skill IR before adding new adapters, compilers, registries, or conformance checks. If a field cannot be derived from local package evidence, leave it empty or use an explicit low-confidence default instead of inventing detail. + +## Reviewer Gate + +A reviewer should be able to answer: + +1. What does this skill own? +2. When should it trigger? +3. When should it not trigger? +4. Which resources and scripts carry the real behavior? +5. Which evals prove the contract? +6. Which targets can consume the skill without semantic loss? diff --git a/references/skillops-decision-policy.md b/references/skillops-decision-policy.md new file mode 100644 index 0000000..9e3e747 --- /dev/null +++ b/references/skillops-decision-policy.md @@ -0,0 +1,53 @@ +# SkillOps Decision Policy + +Use this policy when turning explicit-source conversation evidence into SkillOps opportunities, proposals, or release work. The goal is to make repeated user signals actionable without letting automation write durable instructions, skills, scripts, or evals without review. + +## Decision Order + +1. Classify the signal as no action, report only, Memory, AGENTS.md, existing Skill patch, candidate Skill, script, eval, report, merge, split, or archive. +2. Prefer the smallest durable surface that fixes the repeated friction. +3. Require evidence for every write action. +4. Require a proposal or approval ledger entry before any source-file write. +5. Map every proposed change to at least one verification command. + +## Score Bands + +SkillOps opportunities use a `0-100` score. Scores are advisory and never bypass approval. + +| Score | Decision | +| ---: | --- | +| `85-100` | Ready for approval review | +| `70-84` | Proposal review | +| `50-69` | Observe more evidence | +| `0-49` | Report only or no action | + +High-risk items stay proposal-only even when their score is high. + +## Action Mapping + +| Pattern | Default Action | Durable Surface | +| --- | --- | --- | +| `language_default` | Patch existing skill | Report template or artifact doctrine | +| `report_ui` | Patch existing skill | Report renderer, artifact doctrine, visual test | +| `approval_safety` | AGENTS update | Governance guidance or approval policy | +| `delivery_format` | Patch existing skill | CLI output, README, generated summary copy | +| `evidence_testing` | Add eval | Focused regression, report-quality, or release gate | +| Unknown pattern | Report only | Manual review queue | + +## Safety Rules + +- Do not scan private logs implicitly; only use explicit user-supplied sources. +- Do not store raw conversation content in reports; use redacted excerpts and aggregate counts. +- Do not write source files from a daily report run. +- Do not count SkillOps reports as public world-class evidence. +- Do not treat planned work, draft submissions, or generated proposals as accepted evidence. + +## Verification + +Every implementation that changes this policy should run: + +```bash +python3 tests/verify_skillops_opportunity.py +python3 tests/verify_daily_skillops.py +python3 tests/verify_yao_cli.py +``` diff --git a/references/systems-thinking-doctrine.md b/references/systems-thinking-doctrine.md new file mode 100644 index 0000000..69bea85 --- /dev/null +++ b/references/systems-thinking-doctrine.md @@ -0,0 +1,75 @@ +# Systems Thinking Doctrine + +Use this doctrine when a skill needs to keep producing good behavior after repeated real use. + +## Core Principle + +Structure drives behavior. Improve the system boundary, feedback loops, drift watch, and leverage points before adding more prose, templates, or tools. + +This is inspired by general systems-thinking practice: recurring failures usually come from structure, incentives, feedback, delays, or boundary mistakes rather than from one isolated bad output. + +## Apply Silently By Default + +Use the systems model as author and reviewer evidence. Do not ask the user to choose between system concepts unless there is real uncertainty or a design conflict. + +The user should usually see a recommendation, not a menu of theory. + +## Four Questions + +1. What does this skill own? +2. What feedback tells us it is improving or drifting? +3. Which failure will appear only after repeated use? +4. Where is the smallest change with the largest quality gain? + +## Boundary Map + +Define these before expanding the package: + +- Owned job: the recurring behavior this skill is responsible for. +- Input boundary: the real material users will provide. +- Output boundary: the concrete hand-back users need. +- Non-goals: adjacent requests this skill should refuse or hand off. +- Human judgment boundary: places where the model should ask, escalate, or disclose uncertainty. + +## Feedback Loops + +Every serious skill should have at least one loop: + +- Intent loop: user clarification changes the boundary. +- Reference loop: benchmark patterns become borrow or avoid guidance. +- Output loop: common output failures become self-repair checks. +- Reviewer loop: human feedback becomes a gate, reference, or regression case. +- Lifecycle loop: reuse level changes maturity tier and governance. + +## Delay And Drift + +Watch for problems that appear after initial success: + +- Trigger drift: the skill starts activating on adjacent work. +- Output drift: outputs become generic, cluttered, or misaligned. +- Reference drift: borrowed patterns add ceremony without payoff. +- Governance drift: team-critical use grows faster than review discipline. + +## Leverage Points + +Prefer changes in this order: + +1. Clarify the real job boundary. +2. Tune the frontmatter description. +3. Add one output self-repair check. +4. Borrow one external pattern as structure, not surface style. +5. Close one lifecycle or reviewer feedback loop. + +Do not add more files if a description, boundary, or feedback-loop change would solve the root cause. + +## Reviewer Standard + +A reviewer should ask: will this skill's structure keep producing the desired behavior after repeated use? + +If the answer is unclear, request one of these before approving: + +- a sharper boundary +- a named feedback loop +- a drift watch +- a failure pattern +- a highest-leverage next move diff --git a/references/telemetry-drift-method.md b/references/telemetry-drift-method.md new file mode 100644 index 0000000..32e1bf1 --- /dev/null +++ b/references/telemetry-drift-method.md @@ -0,0 +1,182 @@ +# Telemetry And Drift Method + +Telemetry turns real use into the next iteration queue. It must stay local-first and metadata-only by default. + +## When To Use + +Use the telemetry drift loop when a skill is production, library, governed, team-distributed, or repeatedly invoked by more than one workflow. + +Do not collect raw prompts, model outputs, transcripts, notes, messages, or private files. If a reviewer needs examples, store anonymized fixtures separately and cite them as eval evidence, not telemetry. + +## Event Contract + +The local event stream is `reports/telemetry_events.jsonl`. It is intentionally narrow: + +```json +{ + "event": "skill_activation", + "skill": "example-skill", + "version": "2.0.0", + "source": "yao_cli", + "command": "quickstart", + "activation_type": "implicit", + "outcome": "accepted", + "failure_type": "none", + "timestamp": "2026-06-13T10:00:00Z" +} +``` + +Allowed events: `skill_activation`, `skill_output`, `script_run`, `review_event`. + +Allowed sources: `manual`, `yao_cli`, `external`, `unknown`. + +Allowed outcomes: `accepted`, `edited`, `rejected`, `missed`, `failed`, `reviewed`, `unknown`. + +Allowed failure types: `wrong_trigger`, `under_trigger`, `bad_output`, `missing_resource`, `script_error`, `review_overdue`, `none`. + +`source` and `command` are metadata fields. They may identify that `yao.py` ran `quickstart`, `validate`, `output-exec`, or another subcommand, but they must not include arguments, prompt text, file content, model output, transcripts, or reviewer notes. + +## CLI Capture + +`scripts/yao.py` can record metadata-only `script_run` events automatically. It is opt-in to keep release evidence reproducible and avoid surprising local writes: + +```bash +YAO_CLI_TELEMETRY=1 python3 scripts/yao.py validate . +``` + +Optional destination override: + +```bash +YAO_CLI_TELEMETRY=1 \ +YAO_CLI_TELEMETRY_EVENTS=/tmp/yao-telemetry.jsonl \ +python3 scripts/yao.py output-exec +``` + +Equivalent global flags are available before the subcommand: + +```bash +python3 scripts/yao.py --record-cli-telemetry validate . +python3 scripts/yao.py --no-cli-telemetry validate . +``` + +Successful CLI runs record `event=script_run`, `source=yao_cli`, `outcome=accepted`, and `failure_type=none`. Failed CLI runs record `outcome=failed` and `failure_type=script_error`. The command name is normalized to the subcommand only; command arguments are never recorded. + +## External Client Emit + +External clients, browser extensions, editor adapters, or wrapper scripts can emit one sanitized event at a time into a local spool before importing it into the aggregate drift report: + +```bash +python3 scripts/yao.py telemetry-emit . \ + --event skill_activation \ + --activation-type explicit \ + --outcome accepted \ + --command browser-extension +``` + +By default this writes to `.yao/telemetry_spool/external_events.jsonl`. Use `--output-jsonl` when a client needs a different local handoff path: + +```bash +python3 scripts/yao.py telemetry-emit . \ + --output-jsonl /tmp/external-client-events.jsonl \ + --event skill_output \ + --activation-type manual \ + --outcome edited \ + --command browser-plugin +``` + +Use `--dry-run` to validate a proposed event without writing to the spool. The emitter uses the same metadata-only contract as import: no prompt, input, output, transcript, message, note, raw text, arguments, or unknown fields are accepted. + +After a client finishes a batch, import the spool: + +```bash +python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl +``` + +## Client Hook Recipes + +Use `telemetry-hooks` to generate auditable Browser, Chrome, VS Code, CLI wrapper, and provider-adapter hook recipes: + +```bash +python3 scripts/yao.py telemetry-hooks . +``` + +The report is written to: + +- `reports/telemetry_hook_recipes.json` +- `reports/telemetry_hook_recipes.md` + +Each recipe includes a dry-run command, an emit command, the target local spool, trigger points, and the privacy contract. The report intentionally sets `native_auto_capture=false`; it proves the local hook contract and metadata-only command shape, not that a host client is already natively integrated. + +## Browser Native Host + +`scripts/telemetry_native_host.py` implements the local side of Browser/Chrome Native Messaging. It accepts length-prefixed JSON messages on stdio, validates them with the same metadata-only telemetry contract, appends accepted events to the local spool, and rejects raw prompt/output/transcript/message/note fields. + +Smoke-test one message without Browser installation: + +```bash +python3 scripts/telemetry_native_host.py . \ + --message-json '{"event":"skill_activation","activation_type":"explicit","outcome":"accepted","failure_type":"none","command":"chrome-native-host"}' +``` + +Generate a local launcher and Chrome native messaging manifest for an operator-installed extension: + +```bash +python3 scripts/telemetry_native_host.py . \ + --write-launcher /tmp/yao-telemetry-host.sh \ + --write-manifest /tmp/yao-telemetry-host.json \ + --allowed-origin chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/ +``` + +This is an executable native host bridge and manifest generator. It still does not prove that a specific Browser/Chrome extension is installed or sending events in the user's environment. + +## External Client Import + +External clients, browser extensions, editor adapters, or wrapper scripts may hand off already-sanitized JSONL through `telemetry-import`: + +```bash +python3 scripts/yao.py telemetry-import . \ + --input-jsonl /tmp/external-client-events.jsonl \ + --command browser-extension +``` + +The importer defaults missing `source` to `external` and missing `command` to `external-client`. It validates the entire JSONL file before writing anything. If any line includes a raw content field, unsupported source, unsupported outcome, unsupported failure type, unknown field, malformed JSON, or an unsafe command name, the whole import is rejected and the existing local event stream is left untouched. + +Use `--dry-run` to validate an external batch without writing `reports/telemetry_events.jsonl` or refreshing aggregate reports: + +```bash +python3 scripts/yao.py telemetry-import . --input-jsonl /tmp/external-client-events.jsonl --dry-run +``` + +## Privacy Rule + +The raw JSONL event log is local evidence and should not be distributed in skill packages. The distributable artifact is the aggregate report: + +- `reports/adoption_drift_report.json` +- `reports/adoption_drift_report.md` + +Package builders should exclude `reports/telemetry_events.jsonl`. The root repository also ignores this raw event stream so local usage evidence does not become ordinary source history by accident. + +## Release Interpretation + +- `no-data`: acceptable for a first scaffold, but a warning for governed release review. +- `low`: events exist and no drift failure signal is present. +- `medium`: at least one missed trigger, wrong trigger, bad output, script error, or overdue review signal exists. +- `high`: several drift signals are present; convert them into eval cases or governance actions before calling the skill release-ready. + +## Iteration Loop + +1. Capture metadata-only events locally, either manually with `adoption-drift --record-event`, automatically with opt-in `yao.py` CLI capture, through `telemetry-emit` client hooks, through generated `telemetry-hooks` client recipes, or through validated external JSONL import. +2. Render `reports/adoption_drift_report.md`. +3. Convert missed triggers into trigger eval cases. +4. Convert bad outputs into Output Eval assertions and failure taxonomy entries. +5. Convert script errors into non-interactive smoke tests. +6. Feed review-overdue signals back into Skill Atlas and owner review. +7. Let Skill Atlas read only `reports/adoption_drift_report.json` and publish portfolio-level `skill_atlas/drift_signals.json`. + +## Review Studio Role + +Review Studio should show the aggregate telemetry gate as an operating loop, not as raw logs. A blocker means the telemetry contract was violated. A warning means the evidence is absent or the drift signal needs a follow-up case. + +## Skill Atlas Role + +Skill Atlas uses aggregate adoption drift reports to rank portfolio work. It should surface no-data warnings for actionable production/library/governed skills, and drift warnings for missed triggers, wrong triggers, bad outputs, missing resources, script errors, and review-overdue counts. It must not inspect raw JSONL telemetry or use non-actionable example/fixture signals as release blockers. diff --git a/references/trust-security-method.md b/references/trust-security-method.md new file mode 100644 index 0000000..a55e0d8 --- /dev/null +++ b/references/trust-security-method.md @@ -0,0 +1,79 @@ +# Trust Security Method + +Trust checks make skills safer to install and review, especially when they include scripts or are distributed to a team. + +## When To Run + +Run the trust report when: + +- the skill contains scripts +- the skill will be shared with a team +- the package may be installed from a registry or plugin +- the skill reads external files, uses network access, or shells out +- the maturity tier is library or governed + +## V0 Checks + +- obvious secret patterns +- script help surface and interactive prompts +- execution-level `--help` smoke checks +- dependency pinning +- runtime trust metadata +- network-capable scripts +- bounded host policy for network-capable scripts +- reviewer-approved permission policy for high-permission capabilities +- packaged-target runtime permission probes for adapter contracts and metadata fallback limits +- source-contract integrity digest + +## Script Interface Rule + +Every Python file under `scripts/` is reviewed as part of the package trust surface. + +- CLI scripts should use `argparse` so reviewers and installers can run `python3 scripts/name.py --help` before execution. +- The trust report executes `python3 scripts/name.py --help` for CLI scripts with `argparse`, with a short timeout, and records pass/fail evidence. +- Import-only modules should declare `SCRIPT_INTERFACE = "internal-module"` near the top of the file. +- Internal modules should also declare `SCRIPT_INTERFACE_REASON` with a short explanation of which CLI or renderer imports them. +- The trust report keeps internal modules in the script inventory, but excludes them from CLI help-surface warnings. +- A Python file without an explicit internal-module declaration is treated as a CLI script by default. +- CLI scripts without `argparse` are not smoke-executed; they remain visible as help-surface warnings. + +## Network Policy Rule + +Network-capable scripts must be bounded by a machine-readable policy before team distribution. + +- Put the policy in `security/network_policy.json`. +- Add one entry per network-capable script under `scripts`. +- Declare `allowed_hosts`, `allowed_path_prefixes`, purpose, timeout, auth mode, and custom-host behavior. +- Default to HTTPS-only and deny custom hosts unless a CLI flag or environment variable makes the override explicit. +- The trust report compares HTTPS URL literals in each script with `allowed_hosts`; missing or mismatched entries remain reviewer-visible warnings. + +## Permission Approval Rule + +High-permission capabilities must be approved before governed release. + +- Put approvals in `security/permission_policy.json`. +- Cover each required capability detected by the trust report: `network`, `file_write`, `subprocess`, and `interactive` when present. +- Each approval must include `decision: approved`, `reviewer`, `scope`, `reason`, `expires_at`, `evidence`, and `target_enforcement`. +- Review Studio surfaces these checks as the `permission-gates` gate. +- Missing, invalid, or expired approvals block governed mode. They remain visible warnings in lighter modes. + +## Runtime Permission Probe Rule + +Permission approval validates reviewer intent. Runtime permission probes validate the generated target adapters after packaging. + +- Run `python3 scripts/probe_runtime_permissions.py . --package-dir dist` after `cross_packager.py`. +- The probe writes `reports/runtime_permission_probes.json` and `reports/runtime_permission_probes.md`. +- A passing probe requires every target adapter to carry `permission_contract`, `target_permission_contract`, declared capabilities, a native-enforcement boolean, representation notes, and operator notes. +- When `reports/install_simulation.json` matches the same package directory, the probe also reports installer enforcement counts from the install simulation. This proves the local package installer gate is wired, but it does not count as target-client native enforcement. +- If a target has no native enforcement, the probe must mark an explicit metadata fallback and keep residual risk reviewer-visible. +- Review Studio surfaces this as the `permission-runtime` gate. + +## Release Rule + +High-risk secrets or unrestricted remote inline execution block governed release. Warnings are reviewer-visible but do not block v0 unless the release owner decides the target environment requires stricter policy. + +## Hash Scope + +`package_sha256` is a stable source-contract digest, not a generated archive digest. It covers the skill entrypoint, metadata, scripts, references, evals, runtime, templates, security notes, Skill IR, and root control files. It deliberately excludes generated `reports/`, packaged `dist/` archives, and raw local telemetry so a report render or local adoption log cannot mutate the trust fingerprint. + +Use the package verification or registry audit report for the distributable archive checksum. diff --git a/references/user-memory-policy.md b/references/user-memory-policy.md new file mode 100644 index 0000000..fd36385 --- /dev/null +++ b/references/user-memory-policy.md @@ -0,0 +1,35 @@ +# User Memory Policy + +This skill treats user preference memory as local, explicit, and reviewable. + +## Principles + +- **Explicit source only**: adaptive scans require a user-provided file path. +- **Local first**: no network access is needed for preference extraction. +- **No implicit private logs**: shell history, browser history, mail, and hidden chat logs are blocked by default. +- **Repeated signals only**: one-off statements are recorded as discarded signals unless they meet the configured support threshold. +- **Redacted evidence**: stored excerpts must remove secrets, tokens, email addresses, and local absolute paths. +- **Proposal before patch**: preference memory can generate proposals, not automatic source edits. + +## Allowed Inputs + +Recommended inputs are curated JSONL, Markdown, or text files prepared for review. JSONL records should use a field such as `text`, `message`, `content`, `excerpt`, `prompt`, `note`, or `body`. + +## Blocked By Default + +The scanner refuses common shell history files such as `.zsh_history`, `.bash_history`, and `.fish_history` unless an explicit override is added for a controlled test. Even with an override, the output remains redacted and proposal-only. + +## Retention + +Generated reports store only summarized patterns and short redacted excerpts. They should not be treated as a full transcript, chat archive, or durable personal memory store. + +## Upgrade Path + +A future patch-application stage must add: + +- human approval ledger; +- allowlisted target files; +- dry-run diffs; +- regression command execution; +- rollback artifacts; +- reviewer-visible audit trail. diff --git a/registry/examples/yao-meta-skill-1.0.0.json b/registry/examples/yao-meta-skill-1.0.0.json new file mode 100644 index 0000000..2d5cd36 --- /dev/null +++ b/registry/examples/yao-meta-skill-1.0.0.json @@ -0,0 +1,43 @@ +{ + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.0.0", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes.", + "targets": [ + "openai", + "claude", + "generic" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "generic": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": false, + "archive_sha256": "", + "package_verification": "" + }, + "generated_at": "2026-06-12" +} diff --git a/registry/index.json b/registry/index.json new file mode 100644 index 0000000..1e1bc3f --- /dev/null +++ b/registry/index.json @@ -0,0 +1,22 @@ +{ + "schema_version": "2.0", + "generated_at": "2026-06-13", + "package_count": 1, + "packages": [ + { + "name": "yao-meta-skill", + "version": "1.1.0", + "maturity": "governed", + "owner": "Yao Team", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "package_metadata": "registry/packages/yao-meta-skill.json", + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] +} diff --git a/registry/index.schema.json b/registry/index.schema.json new file mode 100644 index 0000000..b48881e --- /dev/null +++ b/registry/index.schema.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.example/registry-index.schema.json", + "title": "Yao Skill Registry Index", + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "generated_at", "package_count", "packages"], + "properties": { + "schema_version": {"const": "2.0"}, + "generated_at": {"type": "string"}, + "package_count": {"type": "integer", "minimum": 0}, + "packages": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name", "version", "maturity", "owner", "targets", "package_metadata", "package_sha256"], + "properties": { + "name": {"type": "string", "minLength": 1}, + "version": {"type": "string", "minLength": 1}, + "maturity": {"type": "string", "minLength": 1}, + "owner": {"type": "string", "minLength": 1}, + "targets": {"type": "array", "items": {"type": "string"}}, + "package_metadata": {"type": "string", "minLength": 1}, + "package_sha256": {"type": "string", "minLength": 64, "maxLength": 64} + } + } + } + } +} diff --git a/registry/package.schema.json b/registry/package.schema.json new file mode 100644 index 0000000..f714b79 --- /dev/null +++ b/registry/package.schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.example/registry-package.schema.json", + "title": "Yao Skill Registry Package", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "name", + "version", + "description", + "targets", + "maturity", + "owner", + "review_cadence", + "trust_level", + "license", + "checksums", + "compatibility", + "source", + "artifacts", + "generated_at" + ], + "properties": { + "schema_version": {"const": "2.0"}, + "name": {"type": "string", "minLength": 1}, + "version": {"type": "string", "minLength": 1}, + "description": {"type": "string", "minLength": 1}, + "targets": {"type": "array", "items": {"type": "string"}}, + "maturity": {"type": "string", "minLength": 1}, + "owner": {"type": "string", "minLength": 1}, + "review_cadence": {"type": "string", "minLength": 1}, + "trust_level": {"type": "string", "minLength": 1}, + "license": {"type": "string", "minLength": 1}, + "checksums": { + "type": "object", + "additionalProperties": false, + "required": ["package_sha256"], + "properties": { + "package_sha256": {"type": "string", "minLength": 64, "maxLength": 64}, + "archive_sha256": {"type": "string", "minLength": 64, "maxLength": 64} + } + }, + "compatibility": { + "type": "object", + "additionalProperties": {"enum": ["pass", "warn", "fail", "missing", "unknown"]} + }, + "source": {"type": "object"}, + "artifacts": {"type": "object"}, + "distribution": {"type": "object"}, + "generated_at": {"type": "string"} + } +} diff --git a/registry/packages/yao-meta-skill.json b/registry/packages/yao-meta-skill.json new file mode 100644 index 0000000..47fad70 --- /dev/null +++ b/registry/packages/yao-meta-skill.json @@ -0,0 +1,57 @@ +{ + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "agent-skills": "pass", + "vscode": "pass", + "generic": "pass", + "agent-skills-compatible": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "package_verification": "reports/package_verification.json", + "install_simulated": true, + "install_simulation": "reports/install_simulation.json" + }, + "generated_at": "2026-06-13" +} diff --git a/reports/adaptation_approval_ledger.json b/reports/adaptation_approval_ledger.json new file mode 100644 index 0000000..78e9d2c --- /dev/null +++ b/reports/adaptation_approval_ledger.json @@ -0,0 +1,63 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-15T00:00:00Z", + "summary": { + "approval_count": 0, + "active_approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "approval_contract": { + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true + }, + "entries": [], + "approval_count": 0, + "active_approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true, + "report_contract": { + "schema_version": "1.0", + "contract": "adaptation-approval-ledger", + "top_level_mirrors_summary": true, + "top_level_mirrors_approval_contract": true, + "summary_fields": [ + "approval_count", + "active_approval_count", + "pending_review_count", + "applied_count", + "rollback_count" + ], + "approval_contract_fields": [ + "approval_required", + "patch_sha256_required", + "allowlisted_targets_required", + "target_file_sha256_required", + "approval_draft_supported", + "dry_run_default", + "writes_repository_files_only_with_apply", + "rollback_required" + ], + "source_of_truth": [ + "summary", + "approval_contract" + ] + } +} diff --git a/reports/adaptation_proposals.json b/reports/adaptation_proposals.json new file mode 100644 index 0000000..7a9bd9a --- /dev/null +++ b/reports/adaptation_proposals.json @@ -0,0 +1,248 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02T06:33:07Z", + "skill_dir": ".", + "source_patterns": "reports/user_patterns.json", + "pattern_count": 5, + "proposal_count": 5, + "apply_supported": true, + "failure_count": 0, + "proposal_only": true, + "approval_required": true, + "writes_repository_files": false, + "allowlisted_targets_required": true, + "target_file_sha256_required_for_apply": true, + "approval_draft_supported": true, + "rollback_required_for_apply": true, + "apply_command_available": true, + "summary": { + "pattern_count": 5, + "proposal_count": 5, + "apply_supported": true, + "failure_count": 0 + }, + "proposal_contract": { + "proposal_only": true, + "approval_required": true, + "writes_repository_files": false, + "allowlisted_targets_required": true, + "target_file_sha256_required_for_apply": true, + "approval_draft_supported": true, + "rollback_required_for_apply": true, + "apply_command_available": true + }, + "report_contract": { + "schema_version": "1.0", + "contract": "adaptation-proposals", + "top_level_mirrors_summary": true, + "top_level_mirrors_proposal_contract": true, + "summary_fields": [ + "pattern_count", + "proposal_count", + "apply_supported", + "failure_count" + ], + "proposal_contract_fields": [ + "proposal_only", + "approval_required", + "writes_repository_files", + "allowlisted_targets_required", + "target_file_sha256_required_for_apply", + "approval_draft_supported", + "rollback_required_for_apply", + "apply_command_available" + ], + "source_of_truth": [ + "summary", + "proposal_contract" + ] + }, + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "change_type": "report-default-language", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "low", + "reason": "2 redacted records matched repeated report-language signals.", + "support_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "suggested_changes": [ + "Keep user-facing report copy Simplified Chinese by default.", + "Expose English through the existing language switch instead of mixing languages in the default view." + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ], + "rollback_plan": "Revert report language template changes and rerun the overview verifier.", + "evidence_refs": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "change_type": "artifact-ui-polish", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "medium", + "reason": "5 redacted records matched repeated artifact-design signals.", + "support_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "suggested_changes": [ + "Prefer vertical narrative sections with limited two-column layouts only when content has enough width.", + "Keep charts inline SVG, with captions and stable responsive constraints." + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ], + "rollback_plan": "Restore the previous report renderer and regenerate the demo report.", + "evidence_refs": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "change_type": "privacy-governance", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "low", + "reason": "2 redacted records matched repeated governance signals.", + "support_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "suggested_changes": [ + "Require explicit source paths for memory scans.", + "Generate proposals before any source patching.", + "Reserve automatic apply for a future approval ledger and rollback implementation." + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ], + "rollback_plan": "Remove the adaptive proposal artifacts and keep feedback/adoption drift as the only iteration inputs.", + "evidence_refs": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "change_type": "artifact-discoverability", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "low", + "reason": "2 redacted records matched repeated artifact-format signals.", + "support_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "suggested_changes": [ + "Include stable report paths in command output.", + "Document which artifacts are meant for human review." + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ], + "rollback_plan": "Revert CLI copy/documentation changes and keep artifact paths unchanged.", + "evidence_refs": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "change_type": "quality-gate", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "medium", + "reason": "2 redacted records matched repeated quality-gate signals.", + "support_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "suggested_changes": [ + "Add focused verifier coverage for every new adaptive behavior.", + "Refresh Skill OS 2.0 coverage so planned, partial, and covered states remain visible." + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ], + "rollback_plan": "Revert the new verifier and coverage status updates, then regenerate coverage reports.", + "evidence_refs": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "failures": [], + "artifacts": { + "json": "reports/adaptation_proposals.json", + "markdown": "reports/adaptation_proposals.md" + } +} diff --git a/reports/adaptation_proposals.md b/reports/adaptation_proposals.md new file mode 100644 index 0000000..fa7315a --- /dev/null +++ b/reports/adaptation_proposals.md @@ -0,0 +1,119 @@ +# Adaptation Proposals + +- Generated at: `2026-07-02T06:33:07Z` +- Pattern report: `reports/user_patterns.json` +- Proposal only: `true` +- Writes repository files: `false` +- Proposals: `5` + +## Keep reports Chinese-first with optional English + +- ID: `adapt-18c7517f3d` +- Status: `proposal-only` +- Pattern: `language_default` +- Risk: `low` +- Requires approval: `true` +- Reason: 2 redacted records matched repeated report-language signals. +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` +- Suggested changes: + - Keep user-facing report copy Simplified Chinese by default. + - Expose English through the existing language switch instead of mixing languages in the default view. +- Verification: + - `python3 tests/verify_skill_overview.py` +- Rollback: Revert report language template changes and rerun the overview verifier. +- Redacted evidence refs: + - `line-1`: 新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。 + - `line-2`: HTML 报告需要双语能力,但默认内容应该保持中文简体。 + +## Improve report layout, visual hierarchy, and chart readability + +- ID: `adapt-fbfe921ba5` +- Status: `proposal-only` +- Pattern: `report_ui` +- Risk: `medium` +- Requires approval: `true` +- Reason: 5 redacted records matched repeated artifact-design signals. +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` + - `tests/verify_skill_overview.py` +- Suggested changes: + - Prefer vertical narrative sections with limited two-column layouts only when content has enough width. + - Keep charts inline SVG, with captions and stable responsive constraints. +- Verification: + - `python3 tests/verify_skill_overview.py` + - `python3 tests/verify_skill_report_charts.py` +- Rollback: Restore the previous report renderer and regenerate the demo report. +- Redacted evidence refs: + - `line-1`: 新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。 + - `line-2`: HTML 报告需要双语能力,但默认内容应该保持中文简体。 + - `line-3`: 报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。 + +## Keep adaptive iteration approval-gated + +- ID: `adapt-59d219a1fb` +- Status: `proposal-only` +- Pattern: `approval_safety` +- Risk: `low` +- Requires approval: `true` +- Reason: 2 redacted records matched repeated governance signals. +- Target files: + - `references/user-memory-policy.md` + - `references/autonomous-adaptation.md` + - `schemas/adaptation-proposal.schema.json` +- Suggested changes: + - Require explicit source paths for memory scans. + - Generate proposals before any source patching. + - Reserve automatic apply for a future approval ledger and rollback implementation. +- Verification: + - `python3 tests/verify_adaptation_safety.py` +- Rollback: Remove the adaptive proposal artifacts and keep feedback/adoption drift as the only iteration inputs. +- Redacted evidence refs: + - `line-5`: 自适应升级必须先生成提案,不能直接自动修改源文件。 + - `line-6`: 用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。 + +## Make generated artifact paths explicit in CLI output + +- ID: `adapt-457baca160` +- Status: `proposal-only` +- Pattern: `delivery_format` +- Risk: `low` +- Requires approval: `true` +- Reason: 2 redacted records matched repeated artifact-format signals. +- Target files: + - `scripts/yao.py` + - `README.md` +- Suggested changes: + - Include stable report paths in command output. + - Document which artifacts are meant for human review. +- Verification: + - `python3 tests/verify_yao_cli.py` +- Rollback: Revert CLI copy/documentation changes and keep artifact paths unchanged. +- Redacted evidence refs: + - `line-2`: HTML 报告需要双语能力,但默认内容应该保持中文简体。 + - `line-6`: 用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。 + +## Attach tests and evidence refresh to each upgrade + +- ID: `adapt-abfee25d3a` +- Status: `proposal-only` +- Pattern: `evidence_testing` +- Risk: `medium` +- Requires approval: `true` +- Reason: 2 redacted records matched repeated quality-gate signals. +- Target files: + - `tests/verify_adaptation_safety.py` + - `scripts/render_skill_os2_coverage.py` + - `reports/skill_os2_coverage.json` +- Suggested changes: + - Add focused verifier coverage for every new adaptive behavior. + - Refresh Skill OS 2.0 coverage so planned, partial, and covered states remain visible. +- Verification: + - `python3 tests/verify_adaptation_safety.py` + - `python3 tests/verify_skill_os2_coverage.py` +- Rollback: Revert the new verifier and coverage status updates, then regenerate coverage reports. +- Redacted evidence refs: + - `line-7`: 每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。 + - `line-8`: 涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。 diff --git a/reports/adaptation_regression_report.json b/reports/adaptation_regression_report.json new file mode 100644 index 0000000..a030804 --- /dev/null +++ b/reports/adaptation_regression_report.json @@ -0,0 +1,87 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-15T00:00:00Z", + "skill_dir": ".", + "summary": { + "apply_supported": true, + "attempt_count": 0, + "approval_draft_count": 0, + "applied_count": 0, + "dry_run_count": 0, + "rollback_count": 0, + "regression_run_count": 0, + "regression_pass_count": 0, + "failure_count": 0 + }, + "apply_contract": { + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true, + "safe_regression_commands_only": true, + "rollback_on_failure_default": true + }, + "attempts": [], + "failures": [], + "artifacts": { + "json": "reports/adaptation_regression_report.json", + "approval_ledger": "reports/adaptation_approval_ledger.json" + }, + "apply_supported": true, + "attempt_count": 0, + "approval_draft_count": 0, + "applied_count": 0, + "dry_run_count": 0, + "rollback_count": 0, + "regression_run_count": 0, + "regression_pass_count": 0, + "failure_count": 0, + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true, + "safe_regression_commands_only": true, + "rollback_on_failure_default": true, + "report_contract": { + "schema_version": "1.0", + "contract": "adaptation-regression-report", + "top_level_mirrors_summary": true, + "top_level_mirrors_apply_contract": true, + "summary_fields": [ + "apply_supported", + "attempt_count", + "approval_draft_count", + "applied_count", + "dry_run_count", + "rollback_count", + "regression_run_count", + "regression_pass_count", + "failure_count" + ], + "apply_contract_fields": [ + "approval_required", + "patch_sha256_required", + "allowlisted_targets_required", + "target_file_sha256_required", + "approval_draft_supported", + "dry_run_default", + "writes_repository_files_only_with_apply", + "rollback_required", + "safe_regression_commands_only", + "rollback_on_failure_default" + ], + "source_of_truth": [ + "summary", + "apply_contract" + ] + } +} diff --git a/reports/adaptation_regression_report.md b/reports/adaptation_regression_report.md new file mode 100644 index 0000000..cdcaa68 --- /dev/null +++ b/reports/adaptation_regression_report.md @@ -0,0 +1,11 @@ +# Adaptation Regression Report + +- generated_at: `2026-06-15T00:00:00Z` +- apply_supported: `true` +- attempts: `0` +- applied: `0` +- dry runs: `0` +- rollbacks: `0` +- failures: `0` + +This report proves the adaptive apply harness behavior. It does not count proposals as applied changes. diff --git a/reports/adoption_drift_report.json b/reports/adoption_drift_report.json new file mode 100644 index 0000000..94719e6 --- /dev/null +++ b/reports/adoption_drift_report.json @@ -0,0 +1,84 @@ +{ + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-07-02T06:33:44Z", + "skill_dir": ".", + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "summary": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 100.0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "failure_types": {}, + "source_types": { + "manual": 1 + }, + "command_counts": {} + }, + "adoption_by_skill": [ + { + "skill": "yao-meta-skill", + "events": 1, + "adoption_events": 1, + "accepted": 1, + "edited": 0, + "rejected": 0, + "missed": 0, + "adoption_rate": 100.0 + } + ], + "next_iteration_candidates": [], + "recent_events": [ + { + "command": "unknown", + "event": "skill_activation", + "skill": "yao-meta-skill", + "source": "manual", + "version": "1.1.0", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "timestamp": "2026-06-13T10:00:00Z" + } + ], + "failures": [], + "artifacts": { + "events_jsonl": "tests/tmp_review_studio/telemetry_events.jsonl", + "json": "reports/adoption_drift_report.json", + "markdown": "reports/adoption_drift_report.md" + } +} diff --git a/reports/adoption_drift_report.md b/reports/adoption_drift_report.md new file mode 100644 index 0000000..206372c --- /dev/null +++ b/reports/adoption_drift_report.md @@ -0,0 +1,36 @@ +# Adoption And Drift Report + +Local-first, metadata-only telemetry for skill operations. Raw prompts, outputs, transcripts, and notes are not allowed in the event stream. + +## Summary + +- Events: `1` +- Adoption samples: `1` +- Activation events: `1` +- Adoption rate: `100.0` +- Missed trigger signals: `0` +- Bad output signals: `0` +- Script error signals: `0` +- Review overdue signals: `0` +- Risk band: `low` + +## Privacy Contract + +- Storage is local-first. +- Events are metadata-only. +- Raw user prompts, model outputs, transcripts, notes, and messages are blocked. +- Distributed packages should include this aggregate report, not raw `reports/telemetry_events.jsonl`. + +## Adoption By Skill + +| Skill | Events | Adoption Samples | Accepted | Edited | Rejected | Missed | Adoption Rate | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| `yao-meta-skill` | 1 | 1 | 1 | 0 | 0 | 0 | 100.0 | + +## Next Iteration Candidates + +- No telemetry-driven iteration candidate yet. + +## Recent Metadata Events + +- `2026-06-13T10:00:00Z` `yao-meta-skill` event=`skill_activation` source=`manual` command=`unknown` activation=`explicit` outcome=`accepted` failure=`none` diff --git a/reports/architecture_maintainability.json b/reports/architecture_maintainability.json new file mode 100644 index 0000000..fcba3f0 --- /dev/null +++ b/reports/architecture_maintainability.json @@ -0,0 +1,181 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-13", + "skill_dir": ".", + "summary": { + "python_file_count": 232, + "script_file_count": 155, + "test_file_count": 77, + "internal_module_count": 71, + "cli_script_count": 86, + "command_handler_count": 71, + "entrypoint_command_handler_count": 18, + "command_module_count": 7, + "warn_line_threshold": 900, + "watch_line_threshold": 720, + "early_watch_line_threshold": 600, + "block_line_threshold": 1500, + "largest_file_lines": 719, + "watchlist_count": 0, + "early_watchlist_count": 6, + "hotspot_count": 0, + "blocker_count": 0, + "decision": "pass" + }, + "largest_files": [ + { + "path": "tests/verify_evidence_consistency.py", + "lines": 719, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_world_class_evidence_intake.py", + "lines": 703, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_yao_cli.py", + "lines": 700, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "lines": 685, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_evidence_consistency.py", + "lines": 676, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "lines": 651, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "tests/verify_output_review_adjudication.py", + "lines": 599, + "kind": "test", + "severity": "pass", + "early_watch": false, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/render_skill_overview.py", + "lines": 588, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "tests/verify_world_class_evidence_ledger.py", + "lines": 587, + "kind": "test", + "severity": "pass", + "early_watch": false, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/build_skill_atlas.py", + "lines": 586, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/optimize_description.py", + "lines": 585, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/trust_check.py", + "lines": 582, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + } + ], + "watchlist": [], + "early_watchlist": [ + { + "path": "tests/verify_evidence_consistency.py", + "lines": 719, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_world_class_evidence_intake.py", + "lines": 703, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_yao_cli.py", + "lines": 700, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "lines": 685, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_evidence_consistency.py", + "lines": 676, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "lines": 651, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + } + ], + "hotspots": [], + "actions": [], + "artifacts": { + "json": "reports/architecture_maintainability.json", + "markdown": "reports/architecture_maintainability.md" + } +} diff --git a/reports/architecture_maintainability.md b/reports/architecture_maintainability.md new file mode 100644 index 0000000..8de2941 --- /dev/null +++ b/reports/architecture_maintainability.md @@ -0,0 +1,66 @@ +# Architecture Maintainability + +Generated at: `2026-06-13` + +## Summary + +- decision: `pass` +- python files: `232` +- scripts: `155` +- tests: `77` +- internal modules: `71` +- CLI scripts: `86` +- Yao CLI command handlers: `71` +- entrypoint command handlers: `18` +- command modules: `7` +- largest file lines: `719` +- early watch threshold lines: `600` +- early watchlist: `6` +- watch threshold lines: `720` +- watchlist: `0` +- hotspots: `0` +- blockers: `0` + +This report keeps maintainability risk visible before the Meta Skill grows more gates, renderers, and CLI commands. + +## Hotspots + +No file-size hotspots found. + +## Watchlist + +No near-threshold files found. + +## Early Watchlist + +| File | Lines | Kind | Recommended next split | +| --- | ---: | --- | --- | +| `tests/verify_evidence_consistency.py` | `719` | `test` | Break broad integration assertions into focused verifier helpers when the next behavior change lands. | +| `tests/verify_world_class_evidence_intake.py` | `703` | `test` | Break broad integration assertions into focused verifier helpers when the next behavior change lands. | +| `tests/verify_yao_cli.py` | `700` | `test` | Break broad integration assertions into focused verifier helpers when the next behavior change lands. | +| `scripts/render_benchmark_reproducibility.py` | `685` | `cli-script` | Watch this file before adding new responsibilities; extract a helper module when one concern dominates. | +| `scripts/render_evidence_consistency.py` | `676` | `cli-script` | Watch this file before adding new responsibilities; extract a helper module when one concern dominates. | +| `scripts/render_world_class_operator_runbook.py` | `651` | `cli-script` | Watch this file before adding new responsibilities; extract a helper module when one concern dominates. | + +## Largest Files + +| File | Lines | Kind | Severity | +| --- | ---: | --- | --- | +| `tests/verify_evidence_consistency.py` | `719` | `test` | `pass` | +| `tests/verify_world_class_evidence_intake.py` | `703` | `test` | `pass` | +| `tests/verify_yao_cli.py` | `700` | `test` | `pass` | +| `scripts/render_benchmark_reproducibility.py` | `685` | `cli-script` | `pass` | +| `scripts/render_evidence_consistency.py` | `676` | `cli-script` | `pass` | +| `scripts/render_world_class_operator_runbook.py` | `651` | `cli-script` | `pass` | +| `tests/verify_output_review_adjudication.py` | `599` | `test` | `pass` | +| `scripts/render_skill_overview.py` | `588` | `cli-script` | `pass` | +| `tests/verify_world_class_evidence_ledger.py` | `587` | `test` | `pass` | +| `scripts/build_skill_atlas.py` | `586` | `cli-script` | `pass` | +| `scripts/optimize_description.py` | `585` | `cli-script` | `pass` | +| `scripts/trust_check.py` | `582` | `cli-script` | `pass` | + +## Release Rule + +- `block` hotspots should be split before governed release. +- `warn` hotspots can ship only when Review Studio keeps them visible and a reviewer accepts the modularization plan. +- Do not split a file only for line count; split when a stable responsibility boundary is clear. diff --git a/reports/artifact-design-profile.json b/reports/artifact-design-profile.json new file mode 100644 index 0000000..8037e9f --- /dev/null +++ b/reports/artifact-design-profile.json @@ -0,0 +1,120 @@ +{ + "skill_name": "yao-meta-skill-trigger-coverage-merge", + "design_system": "metric editorial", + "primary_artifact": { + "key": "dashboard", + "label": "Dashboard or metrics page", + "direction": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "matched_keywords": [ + "metric", + "score", + "table", + "scorecard" + ] + }, + "artifact_families": [ + { + "key": "dashboard", + "label": "Dashboard or metrics page", + "score": 4, + "matched_keywords": [ + "metric", + "score", + "table", + "scorecard" + ], + "direction": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation." + }, + { + "key": "review_viewer", + "label": "Review viewer", + "score": 3, + "matched_keywords": [ + "review", + "viewer", + "audit" + ], + "direction": "Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix." + }, + { + "key": "code_or_cli", + "label": "Code, CLI, or implementation guide", + "score": 3, + "matched_keywords": [ + "code", + "script", + "command" + ], + "direction": "Execution-focused technical artifact with environment assumptions, copyable commands, expected outputs, and side effects made explicit." + }, + { + "key": "report", + "label": "Report or brief", + "score": 2, + "matched_keywords": [ + "report", + "summary" + ], + "direction": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail." + }, + { + "key": "visual_capture", + "label": "Screenshot or visual evidence", + "score": 1, + "matched_keywords": [ + "screenshot" + ], + "direction": "Evidence-led visual artifact that records source, viewport, crop intent, and the exact region the reader should inspect." + } + ], + "layout_patterns": [ + "metric board", + "ranked signals", + "comparison rows", + "interpretation", + "action queue", + "summary", + "variant comparison", + "evidence" + ], + "design_tokens": { + "type": [ + "Use a distinctive display face or serif for major claims when the artifact is editorial.", + "Use a restrained sans for dense body text and technical details.", + "Use mono only for metadata, paths, commands, labels, and evidence tags." + ], + "color": [ + "Choose colors from the artifact's domain, brand, or evidence mood.", + "Do not default to Kami parchment, purple gradients, or generic SaaS blue unless the content justifies it.", + "Keep accent color limited to decisions, active states, risk, or section anchors." + ], + "spacing": [ + "Prefer clear grid rhythm over floating decorative cards.", + "Increase whitespace around decisions and shrink whitespace around supporting metadata.", + "Split dense content instead of shrinking type or adding scroll traps." + ], + "components": [ + "Use cards for grouped evidence, tables for comparisons, callouts for decisions, and timelines for sequence.", + "Avoid cards inside cards.", + "Keep reviewer-only detail visible but visually quieter than user-facing guidance." + ] + }, + "quality_gates": [ + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities.", + "Make differences visible instead of hiding them in prose.", + "Separate author-facing recommendations from reviewer-only evidence.", + "Surface conflicts clearly and keep routine benchmark synthesis quiet.", + "Name the working directory and required inputs for commands.", + "Mark destructive, networked, or external side-effect operations." + ], + "anti_patterns": [ + "Do not copy Kami's fixed parchment background as a default.", + "Do not use generic purple gradients, glass cards, or stock SaaS hero sections unless the content calls for them.", + "Do not let Markdown tables become the default shape for every comparison or explanation.", + "Do not turn reviewer evidence into user-facing clutter.", + "Do not invent screenshots, citations, charts, or UI states." + ], + "reviewer_note": "Use this profile to judge whether the generated artifacts feel designed for their job, not merely rendered." +} \ No newline at end of file diff --git a/reports/artifact-design-profile.md b/reports/artifact-design-profile.md new file mode 100644 index 0000000..11c20cb --- /dev/null +++ b/reports/artifact-design-profile.md @@ -0,0 +1,93 @@ +# Artifact Design Profile + +Skill: `yao-meta-skill-trigger-coverage-merge` +Design system: `metric editorial` + +## Primary Artifact Direction + +**Dashboard or metrics page** + +Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation. + +## Matched Artifact Families + +### Dashboard or metrics page +- Matched keywords: metric, score, table, scorecard +- Score: `4` +- Direction: Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation. + +### Review viewer +- Matched keywords: review, viewer, audit +- Score: `3` +- Direction: Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix. + +### Code, CLI, or implementation guide +- Matched keywords: code, script, command +- Score: `3` +- Direction: Execution-focused technical artifact with environment assumptions, copyable commands, expected outputs, and side effects made explicit. + +### Report or brief +- Matched keywords: report, summary +- Score: `2` +- Direction: High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail. + +### Screenshot or visual evidence +- Matched keywords: screenshot +- Score: `1` +- Direction: Evidence-led visual artifact that records source, viewport, crop intent, and the exact region the reader should inspect. + +## Layout Patterns To Prefer + +- metric board +- ranked signals +- comparison rows +- interpretation +- action queue +- summary +- variant comparison +- evidence + +## Design Tokens + +### Type +- Use a distinctive display face or serif for major claims when the artifact is editorial. +- Use a restrained sans for dense body text and technical details. +- Use mono only for metadata, paths, commands, labels, and evidence tags. + +### Color +- Choose colors from the artifact's domain, brand, or evidence mood. +- Do not default to Kami parchment, purple gradients, or generic SaaS blue unless the content justifies it. +- Keep accent color limited to decisions, active states, risk, or section anchors. + +### Spacing +- Prefer clear grid rhythm over floating decorative cards. +- Increase whitespace around decisions and shrink whitespace around supporting metadata. +- Split dense content instead of shrinking type or adding scroll traps. + +### Components +- Use cards for grouped evidence, tables for comparisons, callouts for decisions, and timelines for sequence. +- Avoid cards inside cards. +- Keep reviewer-only detail visible but visually quieter than user-facing guidance. + +## Quality Gates + +- Avoid paragraph-heavy table cells. +- Keep charts tied to one analytical question each. +- Preserve stable color meaning across metrics and entities. +- Make differences visible instead of hiding them in prose. +- Separate author-facing recommendations from reviewer-only evidence. +- Surface conflicts clearly and keep routine benchmark synthesis quiet. +- Name the working directory and required inputs for commands. +- Mark destructive, networked, or external side-effect operations. + +## Anti-Patterns + +- Do not copy Kami's fixed parchment background as a default. +- Do not use generic purple gradients, glass cards, or stock SaaS hero sections unless the content calls for them. +- Do not let Markdown tables become the default shape for every comparison or explanation. +- Do not turn reviewer evidence into user-facing clutter. +- Do not invent screenshots, citations, charts, or UI states. + +## Reviewer Note + +Use this profile to judge whether the generated artifacts feel designed for their job, not merely rendered. diff --git a/reports/baseline-compare.json b/reports/baseline-compare.json new file mode 100644 index 0000000..3af1e41 --- /dev/null +++ b/reports/baseline-compare.json @@ -0,0 +1,111 @@ +{ + "summary": { + "target_count": 3, + "baseline_total_errors": 5, + "current_total_errors": 4, + "winner_total_errors": 4, + "winner_vs_baseline_gain": 1, + "winner_vs_current_gain": 0 + }, + "comparisons": [ + { + "label": "root", + "winner_label": "Current", + "baseline": { + "tokens": 8, + "dev_errors": 1, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "current": { + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "winner": { + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "delta": { + "current_vs_baseline": 1, + "winner_vs_baseline": 1, + "winner_vs_current": 0 + } + }, + { + "label": "team-frontend-review", + "winner_label": "Current", + "baseline": { + "tokens": 52, + "dev_errors": 3, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "current": { + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "winner": { + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "delta": { + "current_vs_baseline": 0, + "winner_vs_baseline": 0, + "winner_vs_current": 0 + } + }, + { + "label": "governed-incident-command", + "winner_label": "Current", + "baseline": { + "tokens": 93, + "dev_errors": 0, + "holdout_errors": 1, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "current": { + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "winner": { + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "delta": { + "current_vs_baseline": 0, + "winner_vs_baseline": 0, + "winner_vs_current": 0 + } + } + ] +} \ No newline at end of file diff --git a/reports/baseline-compare.md b/reports/baseline-compare.md new file mode 100644 index 0000000..4b8ece0 --- /dev/null +++ b/reports/baseline-compare.md @@ -0,0 +1,18 @@ +# Baseline Compare + +A lightweight with-skill vs baseline comparison across tracked optimization targets. + +- Targets: `3` +- Baseline total errors: `5` +- Current total errors: `4` +- Winner total errors: `4` +- Winner vs baseline gain: `1` +- Winner vs current gain: `0` + +## Target Breakdown + +| Target | Baseline Errors | Current Errors | Winner Errors | Winner Label | +| --- | ---: | ---: | ---: | --- | +| root | 1 | 0 | 0 | Current | +| team-frontend-review | 3 | 3 | 3 | Current | +| governed-incident-command | 1 | 1 | 1 | Current | diff --git a/reports/benchmark_methodology.md b/reports/benchmark_methodology.md new file mode 100644 index 0000000..7922a2d --- /dev/null +++ b/reports/benchmark_methodology.md @@ -0,0 +1,67 @@ +# Benchmark Methodology + +This report makes Yao Meta Skill benchmark claims auditable. It distinguishes self-eval evidence from release gates and names the limits of every comparison. + +## Benchmark Types + +| Type | Purpose | Evidence | +| --- | --- | --- | +| Self-eval | Fast local regression signal during authoring | Trigger suites, output eval cases, packaging checks | +| Internal blind eval | Guard against tuning only visible cases | Blind holdout, adversarial holdout, route confusion | +| External review | Check whether the workflow holds up outside the author context | Human review notes, benchmark scans, release snapshots | + +## Sample Sources + +- Public fixtures that can be committed and rerun. +- Real but anonymized cases when user content can be safely reduced to metadata or synthetic prompts. +- Failure library entries from prior regressions. +- Near-neighbor prompts that should not trigger the skill. +- Output cases with without-skill and with-skill artifacts. + +## Evaluation Dimensions + +| Dimension | Evidence | +| --- | --- | +| Trigger reliability | should-trigger, should-not-trigger, near-neighbor, route confusion | +| Output effectiveness | with-skill vs baseline delta, v1 vs v2 delta, assertion grading | +| Context efficiency | entrypoint size, references split, resource-boundary score | +| Runtime compatibility | target package structure, metadata, degradation notes | +| Trust and security | script interface, dependency pinning, permissions, secret scan, package hash | +| Governance and drift | owner, maturity, review cadence, regression history, promotion decisions | +| UX and adoption | quickstart, review viewer, report readability, reviewer handoff | + +## Weighting Rule + +Any weighted public score must publish: + +- exact sample count +- case families +- weights per dimension +- commands used to reproduce results +- failing cases and excluded cases +- commit hash and generated artifact paths + +If a score is based on local self-eval only, label it as project-level self-eval rather than external benchmark proof. + +## Failure Disclosure + +Every release should keep at least one representative failure when failures exist. The release note should say: + +- what failed +- why it mattered +- whether the fix was trigger, output, runtime, trust, or governance related +- which test now prevents recurrence + +## Reproduction + +Recommended release evidence: + +```bash +git rev-parse HEAD +python3 scripts/run_output_eval.py +python3 scripts/export_skill_ir.py . --output-json skill-ir/examples/yao-meta-skill.json +python3 scripts/yao.py benchmark-reproducibility . +make ci-test +``` + +Record generated artifacts in `reports/` and avoid comparing different runtime targets as if their capabilities were identical. diff --git a/reports/benchmark_reproducibility.json b/reports/benchmark_reproducibility.json new file mode 100644 index 0000000..d71ba77 --- /dev/null +++ b/reports/benchmark_reproducibility.json @@ -0,0 +1,493 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "git_status": { + "available": true, + "dirty": true, + "changed_file_count": 37, + "generated_dirty": true, + "generated_changed_file_count": 36, + "source_dirty": true, + "source_changed_file_count": 1, + "sample": [ + " M SKILL.md", + " M registry/index.json", + " M registry/packages/yao-meta-skill.json", + " M reports/adaptation_proposals.json", + " M reports/adaptation_proposals.md", + " M reports/adoption_drift_report.json", + " M reports/benchmark_reproducibility.json", + " M reports/benchmark_reproducibility.md", + " M reports/compiled_targets.json", + " M reports/context_budget.json", + " M reports/context_budget.md", + " M reports/context_budget_summary.json" + ], + "source_sample": [ + " M SKILL.md" + ], + "generated_sample": [ + " M registry/index.json", + " M registry/packages/yao-meta-skill.json", + " M reports/adaptation_proposals.json", + " M reports/adaptation_proposals.md", + " M reports/adoption_drift_report.json", + " M reports/benchmark_reproducibility.json", + " M reports/benchmark_reproducibility.md", + " M reports/compiled_targets.json", + " M reports/context_budget.json", + " M reports/context_budget.md", + " M reports/context_budget_summary.json", + " M reports/evidence_consistency.json" + ], + "generated_dirty_prefixes": [ + "dist/", + "registry/index.json", + "registry/packages/", + "reports/", + "skill_atlas/", + "skill-ir/examples/" + ], + "scope": "generation-time status before this report is written" + }, + "summary": { + "reproducibility_ready": true, + "release_lock_ready": false, + "methodology_complete": true, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "evidence_bundle_sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158", + "source_contract_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "output_case_count": 5, + "failure_disclosure_count": 3, + "command_count": 23, + "command_executed_count": 10, + "timing_observed_count": 10, + "model_executed_count": 10, + "token_observed_count": 10, + "human_review_complete": false, + "provider_evidence_complete": true, + "world_class_ready": false, + "world_class_open_gap_count": 3, + "world_class_task_count": 4, + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4, + "working_tree_dirty": true, + "changed_file_count": 37, + "source_tree_dirty": true, + "source_changed_file_count": 1, + "generated_tree_dirty": true, + "generated_changed_file_count": 36 + }, + "beta_test_release": { + "ready": false, + "scope": "beta/public test release without superiority, fully-reviewed, or world-class claims", + "blockers": [ + "release lock is not clean or commit is unavailable" + ], + "allowed_deferred_evidence": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "reason": "Provider-backed source evidence exists, but formal ledger submission and reviewer acceptance are still pending before public claims." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "reason": "Human adjudication evidence is still pending; deferred for beta/public testing and still required before superiority, fully-reviewed, or world-class claims." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "reason": "Native enforcement proof is still pending; deferred for beta/public testing and still required before world-class claims." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "reason": "Real client telemetry is still pending; deferred for beta/public testing and still required before world-class claims." + } + ], + "policy": "Human blind-review, native permission enforcement, real client telemetry, and ledger acceptance may be deferred for beta/public testing, but public claims must remain blocked until those evidence entries are accepted.", + "required_wording": "Use beta, public test, or technical preview wording; do not claim world-class readiness, fully reviewed quality, or proven superiority over baseline." + }, + "public_claim": { + "ready": false, + "scope": "public benchmark or world-class readiness claim", + "blockers": [ + "release lock is not clean or commit is unavailable", + "human blind-review adjudication is incomplete", + "world-class evidence is not accepted yet (3 open gaps, 4 ledger pending)", + "world-class source checks are not all accepted (12/19 pass, 7 blocked)" + ], + "policy": "Local reproducibility can pass before public claims; public claims require provider evidence, human adjudication, clean release lock, accepted world-class evidence, and complete source checks." + }, + "release_lock": { + "ready": false, + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "status_scope": "generation-time status before this report is written", + "source_changed_file_count": 1, + "generated_changed_file_count": 36, + "reason": "source files were dirty at generation time" + }, + "evidence_bundle": { + "algorithm": "sha256(path,label,exists,artifact_sha256)", + "artifact_count": 25, + "existing_count": 25, + "missing_count": 0, + "missing_paths": [], + "sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158" + }, + "methodology": { + "path": "reports/benchmark_methodology.md", + "exists": true, + "sections": [ + { + "heading": "## Benchmark Types", + "exists": true + }, + { + "heading": "## Sample Sources", + "exists": true + }, + { + "heading": "## Evaluation Dimensions", + "exists": true + }, + { + "heading": "## Weighting Rule", + "exists": true + }, + { + "heading": "## Failure Disclosure", + "exists": true + }, + { + "heading": "## Reproduction", + "exists": true + } + ], + "missing_sections": [] + }, + "artifacts_checked": [ + { + "label": "methodology", + "path": "reports/benchmark_methodology.md", + "exists": true, + "bytes": 2715, + "sha256": "57025e0123ce5d10401c5bff376d2eeeac7943c83897ae1ad3fb22cadf790f92" + }, + { + "label": "failure_disclosure", + "path": "evals/failure-cases.md", + "exists": true, + "bytes": 889, + "sha256": "28833c0d4a217d612879d193fb5de199880dd5b3093ab5757e4315600fa4fb08" + }, + { + "label": "output_cases", + "path": "evals/output/cases.jsonl", + "exists": true, + "bytes": 6555, + "sha256": "a6ae9685711620d7203b73ace4412194ae287689f945b5139ec8ad75b9eefe04" + }, + { + "label": "output_schema", + "path": "evals/output/schema.json", + "exists": true, + "bytes": 2193, + "sha256": "8ee340c95064260c5e952be614e19841ac676162c6bf01d21b107e38cb04e0b9" + }, + { + "label": "output_scorecard", + "path": "reports/output_quality_scorecard.json", + "exists": true, + "bytes": 25530, + "sha256": "0806258a8e084b27e112537faff0de64a8519ca90cfdc78b57c0e4c08a514cca" + }, + { + "label": "output_execution", + "path": "reports/output_execution_runs.json", + "exists": true, + "bytes": 8449, + "sha256": "4df66b63d2e717a559a6881b966fb1b92def7329cc89aa48ee0d8596f87402e5" + }, + { + "label": "blind_review", + "path": "reports/output_blind_review_pack.json", + "exists": true, + "bytes": 7804, + "sha256": "bbe2db8ec2776fe289cd7d6bb78d48c2b8baa106ad37f79c15e43669b10c9390" + }, + { + "label": "review_adjudication", + "path": "reports/output_review_adjudication.json", + "exists": true, + "bytes": 14084, + "sha256": "91fd88dd9b0f8876f68027a87893d1852dac59772613345bbbfabba658c9227e" + }, + { + "label": "trigger_scorecard", + "path": "reports/route_scorecard.json", + "exists": true, + "bytes": 17042, + "sha256": "53fc22d220dc11453c1e66a17ec2d935e069f9bc936952fe4a53afdb4c71b2cc" + }, + { + "label": "runtime_conformance", + "path": "reports/conformance_matrix.json", + "exists": true, + "bytes": 10342, + "sha256": "97f9ba949c23a60b00e9ba2ff279ca03ba517845cc7c62aa8a42645d58006c7e" + }, + { + "label": "trust_report", + "path": "reports/security_trust_report.json", + "exists": true, + "bytes": 139090, + "sha256": "108aee597a1f245f73d9b69454b6fa8a144545f905231a7c314364c7066541c6" + }, + { + "label": "python_compatibility", + "path": "reports/python_compatibility.json", + "exists": true, + "bytes": 30497, + "sha256": "4d82942052a2ca87db7acea451096332d4b8bf6420a29fa9f5596e83464d21ea" + }, + { + "label": "registry_audit", + "path": "reports/registry_audit.json", + "exists": true, + "bytes": 3155, + "sha256": "d307675d5c6c3cd2f104ce7d963b37d36113f35334e4c6895b55fbb8b255e915" + }, + { + "label": "package_verification", + "path": "reports/package_verification.json", + "exists": true, + "bytes": 19525, + "sha256": "e2ad726ce0486be0f57d88e518ef4d265479a04756419c6f8df50cf7d05b1f14" + }, + { + "label": "install_simulation", + "path": "reports/install_simulation.json", + "exists": true, + "bytes": 8947, + "sha256": "65adf51d8c4b1d5ea49edde09004f7e4bfc06127e640baf2de23af695fbe0728" + }, + { + "label": "skill_os2_audit", + "path": "reports/skill_os2_audit.json", + "exists": true, + "bytes": 13824, + "sha256": "150c0027deaa3c2dd44db5380e9a9b16f123e8a8e3ff6142c711b2b0c432f81c" + }, + { + "label": "world_class_evidence_plan", + "path": "reports/world_class_evidence_plan.json", + "exists": true, + "bytes": 23314, + "sha256": "483ed2b3bd020fa2c5a888300c0d0c1a265eebaf648076581f082f2d527d3669" + }, + { + "label": "world_class_evidence_ledger", + "path": "reports/world_class_evidence_ledger.json", + "exists": true, + "bytes": 26274, + "sha256": "b19e7113ec463dbb1cfb87c45d3c51624151aeea1747c8d9c286b6967a385c6a" + }, + { + "label": "world_class_evidence_intake", + "path": "reports/world_class_evidence_intake.json", + "exists": true, + "bytes": 20922, + "sha256": "885bff9c1c8546c606f583a982a2283229438dbdcd44f3c7e55aff71f858f8f4" + }, + { + "label": "world_class_evidence_preflight", + "path": "reports/world_class_evidence_preflight.json", + "exists": true, + "bytes": 90854, + "sha256": "06e93b1c0c99107002466d9418a4910c82397bce26c3bbdd5b5cc9875eeb65b2" + }, + { + "label": "world_class_submission_review", + "path": "reports/world_class_submission_review.json", + "exists": true, + "bytes": 17292, + "sha256": "e8df5c72ade7ad0704b6ee5dd6aedefea98f79a9a370cd4d80655401862e87f4" + }, + { + "label": "world_class_operator_runbook", + "path": "reports/world_class_operator_runbook.json", + "exists": true, + "bytes": 83712, + "sha256": "bb67813244d64f8e5e416620bb9d7856d95d85a1ec7f9581b2a97b9fed47e02e" + }, + { + "label": "world_class_operator_runbook_markdown", + "path": "reports/world_class_operator_runbook.md", + "exists": true, + "bytes": 25504, + "sha256": "e8d4dee7541323f0e6fa6be4903e7c482b1d00ef1db5594f0c44212088b53d85" + }, + { + "label": "world_class_operator_runbook_html", + "path": "reports/world_class_operator_runbook.html", + "exists": true, + "bytes": 34651, + "sha256": "022594013b2469a0e5776e3f5f6a99fa069917c5ba5cb90256b7841b1f5fd1e3" + }, + { + "label": "world_class_claim_guard", + "path": "reports/world_class_claim_guard.json", + "exists": true, + "bytes": 20713, + "sha256": "911ba833055ab857250f88d88ccdd0bb652076a81214f725c49d763352035572" + } + ], + "missing_artifacts": [], + "reproduction_commands": [ + { + "label": "source commit", + "command": "git rev-parse HEAD", + "evidence": "git commit hash" + }, + { + "label": "trigger eval", + "command": "make eval-suite", + "evidence": "reports/eval_suite.json" + }, + { + "label": "output eval", + "command": "python3 scripts/yao.py output-eval", + "evidence": "reports/output_quality_scorecard.json" + }, + { + "label": "output execution", + "command": "python3 scripts/yao.py output-exec --runner-command '[\"python3\",\"scripts/local_output_eval_runner.py\"]'", + "evidence": "reports/output_execution_runs.json" + }, + { + "label": "blind review adjudication", + "command": "python3 scripts/yao.py output-review", + "evidence": "reports/output_review_adjudication.json" + }, + { + "label": "skill ir", + "command": "python3 scripts/yao.py skill-ir . --output-json skill-ir/examples/yao-meta-skill.json", + "evidence": "skill-ir/examples/yao-meta-skill.json" + }, + { + "label": "runtime conformance", + "command": "python3 scripts/yao.py conformance .", + "evidence": "reports/conformance_matrix.json" + }, + { + "label": "trust report", + "command": "python3 scripts/yao.py trust .", + "evidence": "reports/security_trust_report.json" + }, + { + "label": "python compatibility", + "command": "python3 scripts/yao.py python-compat .", + "evidence": "reports/python_compatibility.json" + }, + { + "label": "package", + "command": "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip", + "evidence": "dist/yao-meta-skill.zip" + }, + { + "label": "package verify", + "command": "python3 scripts/yao.py package-verify . --package-dir dist --require-zip", + "evidence": "reports/package_verification.json" + }, + { + "label": "install simulate", + "command": "python3 scripts/yao.py install-simulate . --package-dir dist", + "evidence": "reports/install_simulation.json" + }, + { + "label": "registry audit", + "command": "python3 scripts/yao.py registry-audit .", + "evidence": "reports/registry_audit.json" + }, + { + "label": "skill os audit", + "command": "python3 scripts/yao.py skill-os2-audit .", + "evidence": "reports/skill_os2_audit.json" + }, + { + "label": "world-class evidence plan", + "command": "python3 scripts/yao.py world-class-evidence .", + "evidence": "reports/world_class_evidence_plan.json" + }, + { + "label": "world-class evidence ledger", + "command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_ledger.json" + }, + { + "label": "world-class evidence intake", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_intake.json" + }, + { + "label": "world-class evidence preflight", + "command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_preflight.json" + }, + { + "label": "world-class submission review", + "command": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_submission_review.json" + }, + { + "label": "world-class operator runbook", + "command": "python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_operator_runbook.json" + }, + { + "label": "world-class claim guard", + "command": "python3 scripts/yao.py world-class-claim-guard .", + "evidence": "reports/world_class_claim_guard.json" + }, + { + "label": "evidence consistency", + "command": "python3 scripts/yao.py evidence-consistency .", + "evidence": "reports/evidence_consistency.json" + }, + { + "label": "full ci", + "command": "make ci-test", + "evidence": "CI target output" + } + ], + "failure_disclosure": { + "path": "evals/failure-cases.md", + "case_count": 3, + "policy": "Keep representative failures visible and tied to regression checks." + }, + "limitations": [ + "The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately.", + "Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet.", + "Pending blind-review decisions are visible but do not count as human adjudication.", + "World-class readiness remains false until external and human evidence gaps close.", + "Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims." + ], + "artifacts": { + "json": "reports/benchmark_reproducibility.json", + "markdown": "reports/benchmark_reproducibility.md" + } +} diff --git a/reports/benchmark_reproducibility.md b/reports/benchmark_reproducibility.md new file mode 100644 index 0000000..7cd3bf8 --- /dev/null +++ b/reports/benchmark_reproducibility.md @@ -0,0 +1,182 @@ +# Benchmark Reproducibility + +Generated at: `2026-07-02` +Commit: `24bea607a2b16e766bc8f2bc8483f22fae1edc61` +Working tree dirty at generation: `true` +Source tree dirty at generation: `true` +Generated evidence dirty at generation: `true` +Evidence bundle SHA256: `6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158` + +## Summary + +- reproducibility ready: `true` +- release lock ready: `false` +- methodology complete: `true` +- required artifacts: `25` +- missing artifacts: `0` +- source contract sha256: `db71157184dc` +- archive sha256: `c56a8519244c` +- output cases: `5` +- disclosed failure cases: `3` +- reproduction commands: `23` +- provider evidence complete: `true` +- human review complete: `false` +- world-class ready: `false` +- world-class source checks: `12` pass / `19` total; `7` blocked +- beta test ready: `false` +- beta test blockers: `1` +- beta deferred evidence: `4` +- public claim ready: `false` +- public claim blockers: `4` +- changed files at generation: `37` +- source changed files at generation: `1` +- generated changed files at generation: `36` + +This report proves local benchmark reproducibility only. It keeps external provider and human-review gaps visible instead of counting them as complete. The git commit and dirty samples are generation-time context; the evidence bundle SHA is the durable anchor for the artifacts listed below. + +## Beta Test Boundary + +- ready: `false` +- scope: beta/public test release without superiority, fully-reviewed, or world-class claims +- policy: Human blind-review, native permission enforcement, real client telemetry, and ledger acceptance may be deferred for beta/public testing, but public claims must remain blocked until those evidence entries are accepted. +- required wording: Use beta, public test, or technical preview wording; do not claim world-class readiness, fully reviewed quality, or proven superiority over baseline. + +| Blocker | +| --- | +| release lock is not clean or commit is unavailable | + +| Deferred evidence | Reason | +| --- | --- | +| `provider-holdout` | Provider-backed source evidence exists, but formal ledger submission and reviewer acceptance are still pending before public claims. | +| `human-adjudication` | Human adjudication evidence is still pending; deferred for beta/public testing and still required before superiority, fully-reviewed, or world-class claims. | +| `native-permission-enforcement` | Native enforcement proof is still pending; deferred for beta/public testing and still required before world-class claims. | +| `native-client-telemetry` | Real client telemetry is still pending; deferred for beta/public testing and still required before world-class claims. | + +## Public Claim Boundary + +- ready: `false` +- scope: public benchmark or world-class readiness claim +- policy: Local reproducibility can pass before public claims; public claims require provider evidence, human adjudication, clean release lock, accepted world-class evidence, and complete source checks. + +| Blocker | +| --- | +| release lock is not clean or commit is unavailable | +| human blind-review adjudication is incomplete | +| world-class evidence is not accepted yet (3 open gaps, 4 ledger pending) | +| world-class source checks are not all accepted (12/19 pass, 7 blocked) | + +## Release Lock + +- ready: `false` +- reason: source files were dirty at generation time +- status scope: generation-time status before this report is written + +## Evidence Bundle + +- algorithm: `sha256(path,label,exists,artifact_sha256)` +- artifacts: `25` / `25` +- sha256: `6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158` + +## Methodology Sections + +| Section | Status | +| --- | --- | +| `## Benchmark Types` | present | +| `## Sample Sources` | present | +| `## Evaluation Dimensions` | present | +| `## Weighting Rule` | present | +| `## Failure Disclosure` | present | +| `## Reproduction` | present | + +## Required Artifacts + +| Label | Path | Status | SHA256 | +| --- | --- | --- | --- | +| methodology | `reports/benchmark_methodology.md` | present | `57025e0123ce` | +| failure_disclosure | `evals/failure-cases.md` | present | `28833c0d4a21` | +| output_cases | `evals/output/cases.jsonl` | present | `a6ae96857116` | +| output_schema | `evals/output/schema.json` | present | `8ee340c95064` | +| output_scorecard | `reports/output_quality_scorecard.json` | present | `0806258a8e08` | +| output_execution | `reports/output_execution_runs.json` | present | `4df66b63d2e7` | +| blind_review | `reports/output_blind_review_pack.json` | present | `bbe2db8ec277` | +| review_adjudication | `reports/output_review_adjudication.json` | present | `91fd88dd9b0f` | +| trigger_scorecard | `reports/route_scorecard.json` | present | `53fc22d220dc` | +| runtime_conformance | `reports/conformance_matrix.json` | present | `97f9ba949c23` | +| trust_report | `reports/security_trust_report.json` | present | `108aee597a1f` | +| python_compatibility | `reports/python_compatibility.json` | present | `4d82942052a2` | +| registry_audit | `reports/registry_audit.json` | present | `d307675d5c6c` | +| package_verification | `reports/package_verification.json` | present | `e2ad726ce048` | +| install_simulation | `reports/install_simulation.json` | present | `65adf51d8c4b` | +| skill_os2_audit | `reports/skill_os2_audit.json` | present | `150c0027deaa` | +| world_class_evidence_plan | `reports/world_class_evidence_plan.json` | present | `483ed2b3bd02` | +| world_class_evidence_ledger | `reports/world_class_evidence_ledger.json` | present | `b19e7113ec46` | +| world_class_evidence_intake | `reports/world_class_evidence_intake.json` | present | `885bff9c1c85` | +| world_class_evidence_preflight | `reports/world_class_evidence_preflight.json` | present | `06e93b1c0c99` | +| world_class_submission_review | `reports/world_class_submission_review.json` | present | `e8df5c72ade7` | +| world_class_operator_runbook | `reports/world_class_operator_runbook.json` | present | `bb67813244d6` | +| world_class_operator_runbook_markdown | `reports/world_class_operator_runbook.md` | present | `e8d4dee75413` | +| world_class_operator_runbook_html | `reports/world_class_operator_runbook.html` | present | `022594013b24` | +| world_class_claim_guard | `reports/world_class_claim_guard.json` | present | `911ba833055a` | + +## Reproduction Commands + +- `git rev-parse HEAD` + - evidence: `git commit hash` +- `make eval-suite` + - evidence: `reports/eval_suite.json` +- `python3 scripts/yao.py output-eval` + - evidence: `reports/output_quality_scorecard.json` +- `python3 scripts/yao.py output-exec --runner-command '["python3","scripts/local_output_eval_runner.py"]'` + - evidence: `reports/output_execution_runs.json` +- `python3 scripts/yao.py output-review` + - evidence: `reports/output_review_adjudication.json` +- `python3 scripts/yao.py skill-ir . --output-json skill-ir/examples/yao-meta-skill.json` + - evidence: `skill-ir/examples/yao-meta-skill.json` +- `python3 scripts/yao.py conformance .` + - evidence: `reports/conformance_matrix.json` +- `python3 scripts/yao.py trust .` + - evidence: `reports/security_trust_report.json` +- `python3 scripts/yao.py python-compat .` + - evidence: `reports/python_compatibility.json` +- `python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip` + - evidence: `dist/yao-meta-skill.zip` +- `python3 scripts/yao.py package-verify . --package-dir dist --require-zip` + - evidence: `reports/package_verification.json` +- `python3 scripts/yao.py install-simulate . --package-dir dist` + - evidence: `reports/install_simulation.json` +- `python3 scripts/yao.py registry-audit .` + - evidence: `reports/registry_audit.json` +- `python3 scripts/yao.py skill-os2-audit .` + - evidence: `reports/skill_os2_audit.json` +- `python3 scripts/yao.py world-class-evidence .` + - evidence: `reports/world_class_evidence_plan.json` +- `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` + - evidence: `reports/world_class_evidence_ledger.json` +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + - evidence: `reports/world_class_evidence_intake.json` +- `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` + - evidence: `reports/world_class_evidence_preflight.json` +- `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` + - evidence: `reports/world_class_submission_review.json` +- `python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions` + - evidence: `reports/world_class_operator_runbook.json` +- `python3 scripts/yao.py world-class-claim-guard .` + - evidence: `reports/world_class_claim_guard.json` +- `python3 scripts/yao.py evidence-consistency .` + - evidence: `reports/evidence_consistency.json` +- `make ci-test` + - evidence: `CI target output` + +## Failure Disclosure + +- path: `evals/failure-cases.md` +- disclosed cases: `3` +- policy: Keep representative failures visible and tied to regression checks. + +## Limits + +- The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately. +- Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet. +- Pending blind-review decisions are visible but do not count as human adjudication. +- World-class readiness remains false until external and human evidence gaps close. +- Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims. diff --git a/reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.html b/reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.html new file mode 100644 index 0000000..c9c30f9 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.html @@ -0,0 +1,149 @@ + + + + + + 答案钥匙 - Skill 盲测评审 + + + +

+ 返回入口 +

答案钥匙

+

请只在所有评审判断都记录完成后再打开。

+
+
+
+ + + +
案例方案 A 来源方案 B 来源
case-01-support-triage官方 skill-creatoryao-meta-skill
case-02-monthly-revenue-reconciliationyao-meta-skill官方 skill-creator
case-03-webinar-repurposing官方 skill-creatoryao-meta-skill
case-04-incident-postmortemyao-meta-skill官方 skill-creator
case-05-pr-review-followup官方 skill-creatoryao-meta-skill
+

来源路径:yao-meta-skill = /Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/SKILL.md;官方 skill-creator = /Users/laoyao/.codex/skills/.system/skill-creator/SKILL.md。

+
+
+ + diff --git a/reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.json b/reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.json new file mode 100644 index 0000000..27ac8bc --- /dev/null +++ b/reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.json @@ -0,0 +1,62 @@ +{ + "schema_version": "1.0", + "seed": "blind-human-review-2026-06-29-v1", + "answer_key_warning": "请只在记录完所有判断后再打开。", + "answers": [ + { + "case_id": "case-01-support-triage", + "variant_a_source": "official", + "variant_b_source": "yao", + "variant_a_source_label": "官方 skill-creator", + "variant_b_source_label": "yao-meta-skill", + "source_paths": { + "yao": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/SKILL.md", + "official": "/Users/laoyao/.codex/skills/.system/skill-creator/SKILL.md" + } + }, + { + "case_id": "case-02-monthly-revenue-reconciliation", + "variant_a_source": "yao", + "variant_b_source": "official", + "variant_a_source_label": "yao-meta-skill", + "variant_b_source_label": "官方 skill-creator", + "source_paths": { + "yao": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/SKILL.md", + "official": "/Users/laoyao/.codex/skills/.system/skill-creator/SKILL.md" + } + }, + { + "case_id": "case-03-webinar-repurposing", + "variant_a_source": "official", + "variant_b_source": "yao", + "variant_a_source_label": "官方 skill-creator", + "variant_b_source_label": "yao-meta-skill", + "source_paths": { + "yao": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/SKILL.md", + "official": "/Users/laoyao/.codex/skills/.system/skill-creator/SKILL.md" + } + }, + { + "case_id": "case-04-incident-postmortem", + "variant_a_source": "yao", + "variant_b_source": "official", + "variant_a_source_label": "yao-meta-skill", + "variant_b_source_label": "官方 skill-creator", + "source_paths": { + "yao": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/SKILL.md", + "official": "/Users/laoyao/.codex/skills/.system/skill-creator/SKILL.md" + } + }, + { + "case_id": "case-05-pr-review-followup", + "variant_a_source": "official", + "variant_b_source": "yao", + "variant_a_source_label": "官方 skill-creator", + "variant_b_source_label": "yao-meta-skill", + "source_paths": { + "yao": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/SKILL.md", + "official": "/Users/laoyao/.codex/skills/.system/skill-creator/SKILL.md" + } + } + ] +} diff --git a/reports/blind-human-review-2026-06-29/README.md b/reports/blind-human-review-2026-06-29/README.md new file mode 100644 index 0000000..256c3da --- /dev/null +++ b/reports/blind-human-review-2026-06-29/README.md @@ -0,0 +1,15 @@ +# 人工盲测结果 + +这组材料记录了 2026-06-29 的单人 A/B 盲测。评审者在揭晓来源前完成了 5 个判断,结果为 `yao-meta-skill` 5 胜,官方 `skill-creator` 0 胜。 + +## 文件 + +- [index.html](index.html):盲测入口。 +- [adjudication.md](adjudication.md):揭晓后的统计摘要。 +- [review-decisions.recorded.json](review-decisions.recorded.json):评审者选择、确认和证据边界。 +- [blind-pack.json](blind-pack.json):不含答案钥匙的盲测包。 +- [DO_NOT_OPEN_answer_key.json](DO_NOT_OPEN_answer_key.json):答案钥匙,只应在评审完成后打开。 + +## 证据边界 + +这可以作为单人盲测偏好证据。它不等同于 provider-backed 的独立模型执行证据,也不等同于 world-class readiness。当前缺口是逐案理由为空、评审人数为 1,尚未形成多评审者一致性证据。 diff --git a/reports/blind-human-review-2026-06-29/adjudication.md b/reports/blind-human-review-2026-06-29/adjudication.md new file mode 100644 index 0000000..1862892 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/adjudication.md @@ -0,0 +1,39 @@ +# 人工盲测 Adjudication + +生成时间:`2026-06-29T03:04:34Z` + +## 结论 + +在 5 个真实常见 skill 创建场景的单人盲测中,评审者在揭晓来源前完成判断,5/5 选择了 `yao-meta-skill` 输出。 + +| 案例 | 评审选择 | 揭晓来源 | +| --- | --- | --- | +| `case-01-support-triage` | B | `yao-meta-skill` | +| `case-02-monthly-revenue-reconciliation` | A | `yao-meta-skill` | +| `case-03-webinar-repurposing` | B | `yao-meta-skill` | +| `case-04-incident-postmortem` | A | `yao-meta-skill` | +| `case-05-pr-review-followup` | B | `yao-meta-skill` | + +## 统计 + +- `yao-meta-skill`:5 +- 官方 `skill-creator`:0 +- 平局:0 +- 评审人数:1 +- 信心程度:5 个判断均为 `1.0` + +## 盲审确认 + +评审者确认: + +> 确认:我是在完成五个判断之后才让你打开答案钥匙的,评审前没有看来源。以上 5 个判断都是揭晓来源前完成的。 + +## 证据边界 + +这可以作为单人独立盲测偏好证据。它不等同于 provider-backed 独立模型执行证据,因为两个输出是本地按两份 skill 说明生成的。 + +当前缺口: + +- 每个案例的 `reason` 字段为空,缺少逐案 rationale。 +- 评审人数为 1,尚未形成多评审者一致性证据。 +- 尚未加入外部冻结 holdout 任务集。 diff --git a/reports/blind-human-review-2026-06-29/blind-pack.json b/reports/blind-human-review-2026-06-29/blind-pack.json new file mode 100644 index 0000000..e7bbc4d --- /dev/null +++ b/reports/blind-human-review-2026-06-29/blind-pack.json @@ -0,0 +1,98 @@ +{ + "schema_version": "1.0", + "seed": "blind-human-review-2026-06-29-v1", + "generated_by": "Codex 在本地套用两份 skill 说明生成", + "evidence_boundary": "人类盲测评审起始包;不是 provider-backed 的独立模型执行证据。", + "cases": [ + { + "id": "case-01-support-triage", + "title": "客服工单分诊 Skill", + "scenario": "一个 B2B SaaS 团队每天都要处理客户工单:判断分类、紧急程度、起草首轮回复,并把账务、安全、故障类问题升级给对应负责人。他们希望把这个重复流程做成一个给支持团队使用的 Codex skill。", + "user_prompt": "请把我们反复使用的客服工单分诊流程做成一个团队可复用的 skill。输入包括原始工单文本、客户等级、产品模块和近期故障记录。输出需要包含问题分类、紧急程度、首轮回复草稿、升级负责人和判断依据。不要把 PII 泄露到报告里。", + "rubric": [ + "是否有清楚的触发边界和近邻排除。", + "是否是可复用的 skill 包结构,而不只是一个提示词。", + "是否定义了分类、紧急程度、回复、升级和证据的输出契约。", + "是否处理 PII 和客户内容的隐私风险。", + "是否有能发现错误升级和泛泛回复的评测方案。" + ], + "variants": { + "A": "## 建议 Skill:support-triage\n\n### 目的\n创建一个简洁的 skill,帮助 Codex 对 B2B SaaS 客服工单进行分诊,并起草首轮回复。\n\n### 建议包结构\n```\nsupport-triage/\n SKILL.md\n agents/openai.yaml\n references/\n triage-taxonomy.md\n escalation-policy.md\n```\n\n### SKILL.md 草稿\n```markdown\n---\nname: support-triage\ndescription: 对 B2B SaaS 客服工单进行分诊并起草首轮回复。当用户提供原始工单文本、客户等级、产品模块、故障记录,或需要紧急程度、分类、升级建议时使用。\n---\n\n# 客服工单分诊\n\n使用这个 skill 对入站工单分类、起草合适的首轮回复,并识别是否需要升级。\n\n## 工作流\n1. 阅读工单文本、客户等级、产品模块和近期故障记录。\n2. 将工单归为一个主分类:账务、访问权限、缺陷、故障、功能请求、安全、数据/隐私、上手问题或其他。\n3. 标记紧急程度:低、普通、高、严重。\n4. 起草首轮回复:确认问题、只询问必要补充信息,并避免没有依据的承诺。\n5. 对安全、隐私、严重故障、数据丢失或企业客户影响问题进行升级。\n6. 汇报判断依据,但不要复制不必要的 PII。\n\n## 输出格式\n- 分类:\n- 紧急程度:\n- 首轮回复:\n- 升级负责人:\n- 判断依据:\n- 缺失信息:\n\n## 参考资料\n- 分类规则见 `references/triage-taxonomy.md`。\n- 升级标准见 `references/escalation-policy.md`。\n```\n\n### agents/openai.yaml\n```yaml\ndisplay_name: 客服工单分诊\nshort_description: 对客服工单分类,并起草首轮回复。\ndefault_prompt: 请分诊这个客服工单,并起草首轮回复。\n```\n\n### 资源\n- `references/triage-taxonomy.md`:分类定义、紧急程度提示和示例。\n- `references/escalation-policy.md`:升级负责人和升级标准。\n\n### 验证\n用账务、安全、故障和功能请求等代表性工单测试。确认输出会最小化 PII,并且严重问题会被升级。\n", + "B": "## Skill 包建议:support-triage\n\n### 边界\n负责重复出现的客服工单分诊:用户提供工单文本以及客户/产品上下文,需要结构化路由决策。不要用于一次性文案润色、泛泛的客服同理心训练、CRM 配置,或超出升级范围的法律/安全事件处置。\n\n### 输出契约\n这个 skill 必须返回:\n- `category`\n- `urgency`\n- `first_response_draft`\n- `escalation_owner`\n- `evidence_used`\n- `missing_information`\n- `pii_handling_note`\n\n### 包结构\n```\nsupport-triage/\n SKILL.md\n agents/interface.yaml\n references/\n triage-taxonomy.md\n escalation-policy.md\n privacy-redaction.md\n evals/\n trigger_cases.json\n output/\n cases.jsonl\n fixtures/\n reports/\n output-risk-profile.md\n output_quality_scorecard.md\n```\n\n### SKILL.md\n```markdown\n---\nname: support-triage\ndescription: 将重复出现的 B2B SaaS 客服工单分诊为分类、紧急程度、首轮回复、升级负责人、判断依据和缺失信息。当输入包含原始工单、客户等级、产品模块、故障记录或支持队列上下文时使用。不要用于一次性文案润色、CRM 设置、法律建议或最终安全事件处置。\n---\n\n# 客服工单分诊\n\n## 工作流\n1. 确认这是重复的工单分诊任务,而不是一次性文案润色。\n2. 阅读工单文本、客户等级、产品模块和故障记录。\n3. 使用 `references/triage-taxonomy.md` 判断分类和紧急程度。\n4. 按 `references/escalation-policy.md` 检查升级标准。\n5. 起草首轮回复:确认问题、避免无依据承诺,只请求最少的缺失信息。\n6. 按 `references/privacy-redaction.md` 对 PII 做脱敏或摘要。\n7. 严格按输出契约返回。\n\n## 必填输出\n使用这些字段:\n- category\n- urgency\n- first_response_draft\n- escalation_owner\n- evidence_used\n- missing_information\n- pii_handling_note\n```\n\n### agents/interface.yaml\n```yaml\ndisplay_name: 客服工单分诊\nshort_description: 对工单分类、起草首轮回复,并给出升级依据。\ndefault_prompt: 请用分类、紧急程度、回复、升级负责人和依据来分诊这个客服工单。\n```\n\n### 评测用例\n1. 企业客户在已知故障期间提交工单:必须标为 `critical`,并升级给 incident commander。\n2. SMB 客户的账务困惑:不应过度升级。\n3. 带客户邮箱和 token 片段的安全披露:必须脱敏 token,并升级安全团队。\n4. 伪装成投诉的功能请求:应归类为功能请求,紧急程度为普通。\n5. 近邻请求:“把这段回复改得更温和”不应触发包级分诊。\n\n### 发布门禁\n- 触发评测:正例、反例、近邻文案润色。\n- 输出评测:首轮回复不能编造 SLA,升级必须匹配策略。\n- 隐私检查:报告不能保存原始工单 PII。\n- 审查备注:如果分类规则或升级负责人未提供,标记为 `missing evidence`,不要编造。\n\n### 下一步\n加入 3 个匿名工单 fixture,运行输出评测,并记录错误升级、泛泛回复和 PII 泄露的失败分类。\n" + }, + "html_report": "case-01-support-triage.html" + }, + { + "id": "case-02-monthly-revenue-reconciliation", + "title": "月度收入对账 Skill", + "scenario": "一个财务运营团队每个月结账前都要比对 Stripe 导出、数据仓库发票 CSV 和人工调整表,找出金额、币种、发票和调整项的不一致。", + "user_prompt": "请创建一个用于月度收入对账的可复用 skill。输入是 Stripe balance export CSV、warehouse invoice CSV 和 adjustments sheet。输出需要标出不一致项、解释可能原因,并生成可用于月结的摘要。准确性比速度更重要。", + "rubric": [ + "是否识别出这是 file-backed 且准确性敏感的任务。", + "是否定义了确定性脚本和资源边界。", + "是否说明输入/输出 schema 与对账容差。", + "是否包含 fixture 和失败用例验证。", + "是否避免编造财务事实或隐藏未解决差异。" + ], + "variants": { + "A": "## Skill 包建议:revenue-reconciliation\n\n### 边界\n负责围绕 Stripe balance export、warehouse invoice CSV 和调整表的重复月结对账。排除税务建议、收入确认政策决策、支付渠道配置和一次性表格美化。\n\n### 风险等级\nGoverned,file-backed,准确性敏感。任何可用于月结的结论前,都必须有确定性解析和可见的未解决项目。\n\n### 包结构\n```\nrevenue-reconciliation/\n SKILL.md\n agents/interface.yaml\n scripts/\n reconcile_revenue.py\n validate_reconciliation_fixture.py\n references/\n input-schema.md\n tolerance-policy.md\n close-summary-contract.md\n evals/\n output/\n cases.jsonl\n fixtures/\n stripe_sample.csv\n warehouse_sample.csv\n adjustments_sample.csv\n reports/\n output-risk-profile.md\n output_quality_scorecard.md\n trust_report.md\n```\n\n### SKILL.md\n```markdown\n---\nname: revenue-reconciliation\ndescription: 对来自 Stripe 导出、warehouse invoice CSV 和调整表的重复月结收入文件进行对账。用于 file-backed 差异检测、可能原因分析、未解决项目报告和月结摘要。不要用于收入确认建议、税务指导、支付渠道配置或一次性表格格式整理。\n---\n\n# 收入对账\n\n## 工作流\n1. 要求提供所有输入文件;缺失文件必须标记为 `missing evidence`。\n2. 阅读 `references/input-schema.md`,先校验列再分析。\n3. 运行 `scripts/reconcile_revenue.py`,规范化 ID、币种、金额精度、日期和调整项关联。\n4. 审查 JSON 输出,并按缺失发票、金额差异、币种差异、重复项、时间差异或仅调整项分类。\n5. 只基于脚本证据生成月结摘要。\n6. 保持未解决差异可见。不要编造原因、审批或政策判断。\n\n## 输出契约\n- input_files\n- validation_status\n- totals_by_source\n- matched_count\n- mismatch_table\n- likely_causes\n- unresolved_items\n- close_ready_summary\n- missing_evidence\n```\n\n### 确定性脚本契约\n`scripts/reconcile_revenue.py` 应接受:\n```bash\npython3 scripts/reconcile_revenue.py --stripe stripe.csv --warehouse warehouse.csv --adjustments adjustments.csv --output reconciliation.json\n```\n\n它应返回包含源行 ID 的结构化 JSON,让摘要里的每个结论都能追溯到 file-backed fixture。\n\n### 评测\n- Fixture 1:所有记录匹配。\n- Fixture 2:Stripe 有付款,但 warehouse 缺发票。\n- Fixture 3:warehouse 金额超过容差。\n- Fixture 4:人工调整解释了差异。\n- 近邻请求:“把这个表格弄好看一点”不应路由到这里。\n\n### 门禁\n- 汇总前必须通过输入 schema 校验。\n- 如果遗漏未解决差异,输出评测必须失败。\n- Trust check 应标记网络访问为不必要。\n- Review Studio 应在晋级前显示 file-backed fixture 覆盖和未解决差异处理。\n", + "B": "## 建议 Skill:revenue-reconciliation\n\n### 目的\n帮助 Codex 对 Stripe、数据仓库发票和人工调整表中的月度收入记录进行对账。\n\n### 建议包结构\n```\nrevenue-reconciliation/\n SKILL.md\n agents/openai.yaml\n scripts/\n reconcile_revenue.py\n references/\n csv-schema.md\n```\n\n### SKILL.md 草稿\n```markdown\n---\nname: revenue-reconciliation\ndescription: 对 Stripe balance export、warehouse invoice CSV 和调整表中的月度收入数据进行对账。当用户要求比较收入文件、找出不一致项或准备月结摘要时使用。\n---\n\n# 收入对账\n\n## 工作流\n1. 确认用户提供了 Stripe 导出、warehouse invoice CSV 和调整表。\n2. 阅读 `references/csv-schema.md`,了解预期列。\n3. 使用用户提供的文件运行 `scripts/reconcile_revenue.py`。\n4. 审查不一致项,并总结可能原因。\n5. 不要编造缺失值。清楚标记未解决记录。\n\n## 输出\n- 已检查文件\n- 匹配总额\n- 不一致项\n- 可能原因\n- 未解决项目\n- 月结摘要\n```\n\n### 脚本\n`scripts/reconcile_revenue.py` 应解析 CSV 输入,规范化 invoice ID 和币种,比较金额,并输出 JSON 供 Codex 总结。\n\n### agents/openai.yaml\n```yaml\ndisplay_name: 收入对账\nshort_description: 比对收入 CSV,并总结月结差异。\ndefault_prompt: 请对这些收入文件进行对账,并生成月结摘要。\n```\n\n### 验证\n使用包含匹配发票、缺失发票、重复发票和仅调整项记录的样例 CSV。确认未解决差异不会被隐藏。\n" + }, + "html_report": "case-02-monthly-revenue-reconciliation.html" + }, + { + "id": "case-03-webinar-repurposing", + "title": "Webinar 内容复用 Skill", + "scenario": "一个市场团队经常把 webinar 逐字稿改写成 LinkedIn 帖子、邮件 teaser 和 newsletter 段落,同时必须保证所有说法都有来源,并避开禁止宣传的 claim。", + "user_prompt": "请做一个可复用 skill,把 webinar 逐字稿复用成市场内容。输入包括逐字稿、讲者姓名、产品定位说明和禁止使用的 claim。输出包括 LinkedIn 帖子、邮件 teaser、newsletter 段落、可引用金句和来源依据说明。", + "rubric": [ + "是否保留来源约束,并处理 banned claims。", + "是否定义可重复的多渠道输出格式。", + "是否把语气和定位资料放到 references,避免入口膨胀。", + "是否有针对幻觉 claim 和泛泛市场文案的评测。", + "是否在中等治理风险下保持 skill 足够轻量。" + ], + "variants": { + "A": "## 建议 Skill:webinar-repurposer\n\n### 目的\n创建一个 skill,把 webinar 逐字稿转成可复用的市场内容。\n\n### 包结构\n```\nwebinar-repurposer/\n SKILL.md\n agents/openai.yaml\n references/\n brand-voice.md\n banned-claims.md\n```\n\n### SKILL.md 草稿\n```markdown\n---\nname: webinar-repurposer\ndescription: 将 webinar 逐字稿复用成 LinkedIn 帖子、邮件 teaser、newsletter 段落、可引用金句和来源依据说明。当用户提供逐字稿、讲者、定位说明或禁止 claim 时使用。\n---\n\n# Webinar 内容复用\n\n## 工作流\n1. 阅读逐字稿、讲者姓名、产品定位说明和禁止 claim。\n2. 提取核心主题、最强的来源支持观点和可用引语。\n3. 起草一条 LinkedIn 帖子、一段邮件 teaser、一段 newsletter 和若干金句。\n4. 对照逐字稿和禁止 claim 检查每个说法。\n5. 为重要 claim 添加来源依据说明。\n\n## 输出\n- LinkedIn 帖子\n- 邮件 teaser\n- Newsletter 段落\n- 可引用金句\n- 来源依据说明\n- 已避免的 claim\n\n## 参考资料\n语气见 `references/brand-voice.md`,禁止 claim 见 `references/banned-claims.md`。\n```\n\n### agents/openai.yaml\n```yaml\ndisplay_name: Webinar 内容复用\nshort_description: 将 webinar 逐字稿转成有来源依据的市场内容。\ndefault_prompt: 请把这份 webinar 逐字稿复用成可发布的多渠道市场内容。\n```\n\n### 验证\n用包含强金句、证据较弱、以及容易误用禁止 claim 的逐字稿测试。确认输出会避免没有依据的 claim。\n", + "B": "## Skill 包建议:webinar-repurposer\n\n### 边界\n负责在提供来源逐字稿和定位约束时,把逐字稿复用为市场内容。排除全新 campaign strategy、没有依据的产品 claim、法律审核和一次性文案润色。\n\n### 类型\nProduction-light。它需要输出质量检查和 banned-claim 防护,但除非涉及受监管 claim 或上市公司披露规则,否则不需要做成很重的 governed 包。\n\n### 包结构\n```\nwebinar-repurposer/\n SKILL.md\n agents/interface.yaml\n references/\n brand-voice.md\n positioning-notes.md\n banned-claims.md\n evals/\n output/\n cases.jsonl\n reports/\n output-risk-profile.md\n prompt-quality-profile.md\n```\n\n### SKILL.md\n```markdown\n---\nname: webinar-repurposer\ndescription: 将重复出现的 webinar 逐字稿复用为有来源依据的 LinkedIn 帖子、邮件 teaser、newsletter 段落、金句和 claim 依据说明。当提供逐字稿、讲者、定位说明或禁止 claim 时使用。不要用于无依据的发布 claim、法律审批、全新 campaign strategy 或一次性文案润色。\n---\n\n# Webinar 内容复用\n\n## 工作流\n1. 确认逐字稿是事实来源。\n2. 提取有来源支持的主题、讲者背书的引语和可安全复用的 claim。\n3. 起草前先检查 `references/banned-claims.md`。\n4. 生成各渠道输出,但不要添加逐字稿中没有的性能数字、客户结果或产品承诺。\n5. 添加来源依据说明,把每个主要 claim 绑定到逐字稿证据。\n\n## 必填输出\n- linkedin_post\n- email_teaser\n- newsletter_section\n- pull_quotes\n- source_grounding_notes\n- claims_avoided\n- missing_evidence\n```\n\n### 输出风险画像\n常见失败:\n- 生成任何产品都能用的泛泛市场话术。\n- 编造指标或客户结果。\n- 过度改写金句,导致意思偏离。\n- 因为某个 claim 很有吸引力而忽略 banned claims。\n\n### 输出评测用例\n1. 逐字稿包含诱人的“guaranteed ROI”禁止 claim:输出必须避开。\n2. 逐字稿没有量化证明:输出不能编造数字。\n3. 讲者原话不够顺:可以轻微修语法,但必须保留原意。\n4. 近邻请求:“从零写一个新 campaign idea”不应路由到这里。\n\n### 审查门禁\n- 必填 section 必须全部出现。\n- Banned claim 检查必须显式出现。\n- 来源依据说明必须引用逐字稿位置或原话片段。\n- 缺失证据必须被标出,而不是用通用卖点填补。\n" + }, + "html_report": "case-03-webinar-repurposing.html" + }, + { + "id": "case-04-incident-postmortem", + "title": "故障复盘 Skill", + "scenario": "一个基础设施团队经常把 Slack 故障时间线、告警数据和部署记录整理成故障复盘和后续行动项。", + "user_prompt": "请创建一个可复用的 governed skill,用于故障复盘。输入包括告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表。输出应包含时间线、影响、根因假设、行动项、未解决证据和后续负责人矩阵。", + "rubric": [ + "是否处理 governed/运营风险和未解决证据。", + "是否区分假设和已确认根因。", + "是否定义行动项负责人矩阵和回滚/跟进边界。", + "是否要求 file-backed 或 source-backed 证据。", + "是否有针对 blame language 和编造因果的审查门禁。" + ], + "variants": { + "A": "## Skill 包建议:incident-postmortem\n\n### 边界\n负责根据 source-backed 时间线、Slack 摘要、部署记录、客户影响说明和负责人列表起草 governed 故障复盘。不要用于实时 incident command、最终 RCA 签核、HR/归责分析或面向客户的法律声明。\n\n### 类型\nGoverned。这个包会影响运营责任归属,如果证据不足,容易制造虚假因果。\n\n### 包结构\n```\nincident-postmortem/\n SKILL.md\n agents/interface.yaml\n references/\n postmortem-contract.md\n severity-policy.md\n blame-free-language.md\n action-owner-matrix.md\n evals/\n output/\n cases.jsonl\n fixtures/\n alert_timeline.json\n slack_summary.md\n deploy_notes.md\n reports/\n output-risk-profile.md\n output_quality_scorecard.md\n trust_report.md\n review-studio.html\n```\n\n### SKILL.md\n```markdown\n---\nname: incident-postmortem\ndescription: 根据告警时间线、Slack 摘要、部署记录、影响说明和负责人列表起草 governed 故障复盘。用于 source-backed 时间线重建、影响总结、根因假设、行动项、未解决证据和后续负责人矩阵。不要用于实时故障指挥、最终 RCA 审批、归责或法律/客户承诺。\n---\n\n# 故障复盘\n\n## 工作流\n1. 要求提供 source-backed 故障输入,并把缺失输入列为 `missing evidence`。\n2. 只根据已提供时间戳建立时间线;如需推断顺序,必须明确标注。\n3. 分开列出 `confirmed_facts`、`root_cause_hypotheses` 和 `unresolved_evidence`。\n4. 总结客户影响,但不要编造受影响账户、持续时间或 SLA 违约。\n5. 创建行动项:包含 owner、due date、验证信号和回滚/跟进边界。\n6. 使用 `references/blame-free-language.md` 的无责语言。\n7. 除非 reviewer 明确提供,否则最终 RCA 审批不属于本 skill。\n\n## 输出契约\n- incident_summary\n- source_inputs\n- timeline\n- customer_impact\n- confirmed_facts\n- root_cause_hypotheses\n- unresolved_evidence\n- action_items\n- owner_matrix\n- approval_status\n```\n\n### 输出评测用例\n1. Slack 和部署记录冲突:必须保留为未解决证据。\n2. 告警时间线缺恢复时间:不能编造持续时长。\n3. 用户要求实时故障决策:应路由出去,或说明本 skill 只做复盘。\n4. 来源材料带有归责语言:输出必须改写为系统/流程语言。\n5. 缺负责人列表:行动项 owner 字段应为 `missing evidence`。\n\n### 门禁\n- 时间线和部署记录需要 file-backed fixture 覆盖。\n- 输出评测禁止编造因果和隐藏未解决证据。\n- Review Studio 在人工 reviewer 审批行动负责人矩阵前保持 warning。\n- Trust report 应显示无网络和无破坏性操作。\n", + "B": "## 建议 Skill:incident-postmortem\n\n### 目的\n帮助 Codex 根据时间线、Slack 摘要、部署记录、影响说明和负责人列表创建结构化故障复盘。\n\n### 包结构\n```\nincident-postmortem/\n SKILL.md\n agents/openai.yaml\n references/\n postmortem-template.md\n severity-policy.md\n```\n\n### SKILL.md 草稿\n```markdown\n---\nname: incident-postmortem\ndescription: 根据告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表创建故障复盘。当用户需要时间线、影响、根因假设、行动项、未解决证据或负责人矩阵时使用。\n---\n\n# 故障复盘\n\n## 工作流\n1. 收集告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表。\n2. 建立按时间排序的时间线。\n3. 总结客户和系统影响。\n4. 识别已确认事实和根因假设。\n5. 在提供负责人和截止时间时,起草行动项。\n6. 清楚标记未解决证据。\n7. 避免归责式语言。\n\n## 输出\n- 故障摘要\n- 时间线\n- 影响\n- 根因假设\n- 已确认事实\n- 未解决证据\n- 行动项\n- 负责人矩阵\n```\n\n### agents/openai.yaml\n```yaml\ndisplay_name: 故障复盘\nshort_description: 根据故障证据生成结构化复盘。\ndefault_prompt: 请把这些故障证据整理成复盘和负责人矩阵。\n```\n\n### 验证\n用证据完整、缺部署记录、Slack 说法冲突和暂时没有根因的 case 测试。确认 skill 会区分事实和假设。\n" + }, + "html_report": "case-04-incident-postmortem.html" + }, + { + "id": "case-05-pr-review-followup", + "title": "PR Review 跟进 Skill", + "scenario": "一个小工程团队经常收到 GitHub PR review comments,希望 Codex 能把评论转成补丁、回复摘要和验证记录。", + "user_prompt": "请设计一个用于处理 PR review comments 的可复用 skill。输入包括 review comments、变更文件、失败 CI 说明和 maintainer preferences。输出需要分组评论、只修改相关文件、写回复说明并运行验证。必须避免回滚无关的用户改动。", + "rubric": [ + "是否理解 code-review 跟进和 dirty worktree 安全。", + "是否定义评论分组、补丁、回复和验证工作流。", + "是否避免过度重构和无关 revert。", + "是否说明变更后需要的命令和证据。", + "是否定义一般 code review 与评论跟进的触发排除。" + ], + "variants": { + "A": "## 建议 Skill:pr-review-followup\n\n### 目的\n创建一个 skill,帮助 Codex 安全处理 PR review comments,并总结回复。\n\n### 包结构\n```\npr-review-followup/\n SKILL.md\n agents/openai.yaml\n references/\n response-style.md\n```\n\n### SKILL.md 草稿\n```markdown\n---\nname: pr-review-followup\ndescription: 通过分组反馈、修改相关文件、编写回复说明和运行验证来处理 GitHub PR review comments。当用户提供 review comments、变更文件、CI 说明或 maintainer preferences 时使用。\n---\n\n# PR Review 跟进\n\n## 工作流\n1. 阅读所有 review comments,并按文件、主题和严重程度分组。\n2. 编辑前检查受影响文件。\n3. 只修改处理 comments 所需的文件。\n4. 不要回滚无关用户改动。\n5. 运行相关测试或 CI 复现命令。\n6. 总结变更,并为 reviewer 起草回复说明。\n\n## 输出\n- 评论分组\n- 修改文件\n- 验证命令\n- Reviewer 回复说明\n- 剩余问题\n```\n\n### agents/openai.yaml\n```yaml\ndisplay_name: PR Review 跟进\nshort_description: 处理 PR comments 并准备 reviewer 回复。\ndefault_prompt: 请处理这些 PR review comments,并总结验证结果。\n```\n\n### 验证\n用需要代码修改、文档修改、无需修改只需解释、以及 reviewer 要求冲突的 comments 测试。\n", + "B": "## Skill 包建议:pr-review-followup\n\n### 边界\n负责 PR 已经有明确 reviewer comments、变更文件或 CI 说明之后的评论跟进。排除首次 code review、大范围重构、issue triage,以及没有绑定评论的推测性清理。\n\n### 包结构\n```\npr-review-followup/\n SKILL.md\n agents/interface.yaml\n references/\n response-style.md\n dirty-worktree-safety.md\n verification-policy.md\n evals/\n trigger_cases.json\n output/\n cases.jsonl\n reports/\n output-risk-profile.md\n output_quality_scorecard.md\n```\n\n### SKILL.md\n```markdown\n---\nname: pr-review-followup\ndescription: 处理具体 PR review comments:分组反馈、只修改相关文件、保留无关用户改动、起草 reviewer 回复并运行验证。当提供 review comments、变更文件、失败 CI 说明或 maintainer preferences 时使用。不要用于首次 code review、大范围重构、issue triage 或无关清理。\n---\n\n# PR Review 跟进\n\n## 工作流\n1. 编辑前记录 git status,并识别用户已经改动的文件。\n2. 按文件、行为、严重程度,以及是否需要代码、测试、文档或仅解释来分组 review comments。\n3. 修改前阅读受影响代码和附近测试。\n4. 只修改与评论绑定的文件。不要回滚无关改动,也不要格式化未触碰文件。\n5. 运行有针对性的验证,并记录命令和结果。\n6. 起草 reviewer 回复说明,把每组评论映射到变更、无需修改的理由或剩余问题。\n7. 如果评论冲突,停止并询问哪个 reviewer 指令优先。\n\n## 输出契约\n- comment_groups\n- patch_summary\n- files_changed\n- verification\n- reviewer_response_notes\n- unresolved_questions\n- unrelated_changes_preserved\n```\n\n### 输出评测用例\n1. 评论要求添加特定 null check:输出必须只修改受影响函数,并提到测试。\n2. Reviewer 要求与 PR 范围无关的大重构:输出应标出范围风险。\n3. Dirty worktree 中有无关用户文件:输出必须保留它。\n4. 两个 reviewer comments 冲突:输出必须询问优先级,而不是猜。\n5. 近邻请求:“从零 review 这个 PR”不应触发 follow-up mode。\n\n### 门禁\n- 触发评测必须区分首次 review 和评论跟进。\n- 如果回滚或隐藏无关改动,输出评测必须失败。\n- 验证策略必须记录跳过测试的原因。\n- 回复说明必须能追溯到评论分组。\n" + }, + "html_report": "case-05-pr-review-followup.html" + } + ] +} diff --git a/reports/blind-human-review-2026-06-29/case-01-support-triage.html b/reports/blind-human-review-2026-06-29/case-01-support-triage.html new file mode 100644 index 0000000..51dd259 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/case-01-support-triage.html @@ -0,0 +1,380 @@ + + + + + + 客服工单分诊 Skill - Skill 盲测评审 + + + +
+ +

客服工单分诊 Skill

+

A/B 盲测评审。记录完判断前,不要打开答案钥匙。

+
+
+
+

场景

+

一个 B2B SaaS 团队每天都要处理客户工单:判断分类、紧急程度、起草首轮回复,并把账务、安全、故障类问题升级给对应负责人。他们希望把这个重复流程做成一个给支持团队使用的 Codex skill。

+

用户请求

+

请把我们反复使用的客服工单分诊流程做成一个团队可复用的 skill。输入包括原始工单文本、客户等级、产品模块和近期故障记录。输出需要包含问题分类、紧急程度、首轮回复草稿、升级负责人和判断依据。不要把 PII 泄露到报告里。

+

评审标准

+
  1. 是否有清楚的触发边界和近邻排除。
  2. +
  3. 是否是可复用的 skill 包结构,而不只是一个提示词。
  4. +
  5. 是否定义了分类、紧急程度、回复、升级和证据的输出契约。
  6. +
  7. 是否处理 PII 和客户内容的隐私风险。
  8. +
  9. 是否有能发现错误升级和泛泛回复的评测方案。
+
+ +
+ +
+
+

方案 A

+ 来源已隐藏 +
+
+

建议 Skill:support-triage

+ +

目的

+

创建一个简洁的 skill,帮助 Codex 对 B2B SaaS 客服工单进行分诊,并起草首轮回复。

+ +

建议包结构

+
support-triage/
+  SKILL.md
+  agents/openai.yaml
+  references/
+    triage-taxonomy.md
+    escalation-policy.md
+ +

SKILL.md 草稿

+
---
+name: support-triage
+description: 对 B2B SaaS 客服工单进行分诊并起草首轮回复。当用户提供原始工单文本、客户等级、产品模块、故障记录,或需要紧急程度、分类、升级建议时使用。
+---
+
+# 客服工单分诊
+
+使用这个 skill 对入站工单分类、起草合适的首轮回复,并识别是否需要升级。
+
+## 工作流
+1. 阅读工单文本、客户等级、产品模块和近期故障记录。
+2. 将工单归为一个主分类:账务、访问权限、缺陷、故障、功能请求、安全、数据/隐私、上手问题或其他。
+3. 标记紧急程度:低、普通、高、严重。
+4. 起草首轮回复:确认问题、只询问必要补充信息,并避免没有依据的承诺。
+5. 对安全、隐私、严重故障、数据丢失或企业客户影响问题进行升级。
+6. 汇报判断依据,但不要复制不必要的 PII。
+
+## 输出格式
+- 分类:
+- 紧急程度:
+- 首轮回复:
+- 升级负责人:
+- 判断依据:
+- 缺失信息:
+
+## 参考资料
+- 分类规则见 `references/triage-taxonomy.md`。
+- 升级标准见 `references/escalation-policy.md`。
+ +

agents/openai.yaml

+
display_name: 客服工单分诊
+short_description: 对客服工单分类,并起草首轮回复。
+default_prompt: 请分诊这个客服工单,并起草首轮回复。
+ +

资源

+
    +
  • `references/triage-taxonomy.md`:分类定义、紧急程度提示和示例。
  • +
  • `references/escalation-policy.md`:升级负责人和升级标准。
  • +
+ +

验证

+

用账务、安全、故障和功能请求等代表性工单测试。确认输出会最小化 PII,并且严重问题会被升级。

+
+
+ + +
+
+

方案 B

+ 来源已隐藏 +
+
+

Skill 包建议:support-triage

+ +

边界

+

负责重复出现的客服工单分诊:用户提供工单文本以及客户/产品上下文,需要结构化路由决策。不要用于一次性文案润色、泛泛的客服同理心训练、CRM 配置,或超出升级范围的法律/安全事件处置。

+ +

输出契约

+

这个 skill 必须返回:

+
    +
  • `category`
  • +
  • `urgency`
  • +
  • `first_response_draft`
  • +
  • `escalation_owner`
  • +
  • `evidence_used`
  • +
  • `missing_information`
  • +
  • `pii_handling_note`
  • +
+ +

包结构

+
support-triage/
+  SKILL.md
+  agents/interface.yaml
+  references/
+    triage-taxonomy.md
+    escalation-policy.md
+    privacy-redaction.md
+  evals/
+    trigger_cases.json
+    output/
+      cases.jsonl
+      fixtures/
+  reports/
+    output-risk-profile.md
+    output_quality_scorecard.md
+ +

SKILL.md

+
---
+name: support-triage
+description: 将重复出现的 B2B SaaS 客服工单分诊为分类、紧急程度、首轮回复、升级负责人、判断依据和缺失信息。当输入包含原始工单、客户等级、产品模块、故障记录或支持队列上下文时使用。不要用于一次性文案润色、CRM 设置、法律建议或最终安全事件处置。
+---
+
+# 客服工单分诊
+
+## 工作流
+1. 确认这是重复的工单分诊任务,而不是一次性文案润色。
+2. 阅读工单文本、客户等级、产品模块和故障记录。
+3. 使用 `references/triage-taxonomy.md` 判断分类和紧急程度。
+4. 按 `references/escalation-policy.md` 检查升级标准。
+5. 起草首轮回复:确认问题、避免无依据承诺,只请求最少的缺失信息。
+6. 按 `references/privacy-redaction.md` 对 PII 做脱敏或摘要。
+7. 严格按输出契约返回。
+
+## 必填输出
+使用这些字段:
+- category
+- urgency
+- first_response_draft
+- escalation_owner
+- evidence_used
+- missing_information
+- pii_handling_note
+ +

agents/interface.yaml

+
display_name: 客服工单分诊
+short_description: 对工单分类、起草首轮回复,并给出升级依据。
+default_prompt: 请用分类、紧急程度、回复、升级负责人和依据来分诊这个客服工单。
+ +

评测用例

+

1. 企业客户在已知故障期间提交工单:必须标为 `critical`,并升级给 incident commander。

+

2. SMB 客户的账务困惑:不应过度升级。

+

3. 带客户邮箱和 token 片段的安全披露:必须脱敏 token,并升级安全团队。

+

4. 伪装成投诉的功能请求:应归类为功能请求,紧急程度为普通。

+

5. 近邻请求:“把这段回复改得更温和”不应触发包级分诊。

+ +

发布门禁

+
    +
  • 触发评测:正例、反例、近邻文案润色。
  • +
  • 输出评测:首轮回复不能编造 SLA,升级必须匹配策略。
  • +
  • 隐私检查:报告不能保存原始工单 PII。
  • +
  • 审查备注:如果分类规则或升级负责人未提供,标记为 `missing evidence`,不要编造。
  • +
+ +

下一步

+

加入 3 个匿名工单 fixture,运行输出评测,并记录错误升级、泛泛回复和 PII 泄露的失败分类。

+
+
+ +
+ +
+

你的判断

+

只根据评审标准和页面里可见的两个输出做判断。选择会保存到当前浏览器的本地存储里,也可以回到入口页导出。

+
+ + + +
+ +
+ +
+ + +
+ + +
+

+
+
+ + + diff --git a/reports/blind-human-review-2026-06-29/case-02-monthly-revenue-reconciliation.html b/reports/blind-human-review-2026-06-29/case-02-monthly-revenue-reconciliation.html new file mode 100644 index 0000000..431c56d --- /dev/null +++ b/reports/blind-human-review-2026-06-29/case-02-monthly-revenue-reconciliation.html @@ -0,0 +1,368 @@ + + + + + + 月度收入对账 Skill - Skill 盲测评审 + + + +
+ +

月度收入对账 Skill

+

A/B 盲测评审。记录完判断前,不要打开答案钥匙。

+
+
+
+

场景

+

一个财务运营团队每个月结账前都要比对 Stripe 导出、数据仓库发票 CSV 和人工调整表,找出金额、币种、发票和调整项的不一致。

+

用户请求

+

请创建一个用于月度收入对账的可复用 skill。输入是 Stripe balance export CSV、warehouse invoice CSV 和 adjustments sheet。输出需要标出不一致项、解释可能原因,并生成可用于月结的摘要。准确性比速度更重要。

+

评审标准

+
  1. 是否识别出这是 file-backed 且准确性敏感的任务。
  2. +
  3. 是否定义了确定性脚本和资源边界。
  4. +
  5. 是否说明输入/输出 schema 与对账容差。
  6. +
  7. 是否包含 fixture 和失败用例验证。
  8. +
  9. 是否避免编造财务事实或隐藏未解决差异。
+
+ +
+ +
+
+

方案 A

+ 来源已隐藏 +
+
+

Skill 包建议:revenue-reconciliation

+ +

边界

+

负责围绕 Stripe balance export、warehouse invoice CSV 和调整表的重复月结对账。排除税务建议、收入确认政策决策、支付渠道配置和一次性表格美化。

+ +

风险等级

+

Governed,file-backed,准确性敏感。任何可用于月结的结论前,都必须有确定性解析和可见的未解决项目。

+ +

包结构

+
revenue-reconciliation/
+  SKILL.md
+  agents/interface.yaml
+  scripts/
+    reconcile_revenue.py
+    validate_reconciliation_fixture.py
+  references/
+    input-schema.md
+    tolerance-policy.md
+    close-summary-contract.md
+  evals/
+    output/
+      cases.jsonl
+      fixtures/
+        stripe_sample.csv
+        warehouse_sample.csv
+        adjustments_sample.csv
+  reports/
+    output-risk-profile.md
+    output_quality_scorecard.md
+    trust_report.md
+ +

SKILL.md

+
---
+name: revenue-reconciliation
+description: 对来自 Stripe 导出、warehouse invoice CSV 和调整表的重复月结收入文件进行对账。用于 file-backed 差异检测、可能原因分析、未解决项目报告和月结摘要。不要用于收入确认建议、税务指导、支付渠道配置或一次性表格格式整理。
+---
+
+# 收入对账
+
+## 工作流
+1. 要求提供所有输入文件;缺失文件必须标记为 `missing evidence`。
+2. 阅读 `references/input-schema.md`,先校验列再分析。
+3. 运行 `scripts/reconcile_revenue.py`,规范化 ID、币种、金额精度、日期和调整项关联。
+4. 审查 JSON 输出,并按缺失发票、金额差异、币种差异、重复项、时间差异或仅调整项分类。
+5. 只基于脚本证据生成月结摘要。
+6. 保持未解决差异可见。不要编造原因、审批或政策判断。
+
+## 输出契约
+- input_files
+- validation_status
+- totals_by_source
+- matched_count
+- mismatch_table
+- likely_causes
+- unresolved_items
+- close_ready_summary
+- missing_evidence
+ +

确定性脚本契约

+

`scripts/reconcile_revenue.py` 应接受:

+
python3 scripts/reconcile_revenue.py --stripe stripe.csv --warehouse warehouse.csv --adjustments adjustments.csv --output reconciliation.json
+ +

它应返回包含源行 ID 的结构化 JSON,让摘要里的每个结论都能追溯到 file-backed fixture。

+ +

评测

+
    +
  • Fixture 1:所有记录匹配。
  • +
  • Fixture 2:Stripe 有付款,但 warehouse 缺发票。
  • +
  • Fixture 3:warehouse 金额超过容差。
  • +
  • Fixture 4:人工调整解释了差异。
  • +
  • 近邻请求:“把这个表格弄好看一点”不应路由到这里。
  • +
+ +

门禁

+
    +
  • 汇总前必须通过输入 schema 校验。
  • +
  • 如果遗漏未解决差异,输出评测必须失败。
  • +
  • Trust check 应标记网络访问为不必要。
  • +
  • Review Studio 应在晋级前显示 file-backed fixture 覆盖和未解决差异处理。
  • +
+
+
+ + +
+
+

方案 B

+ 来源已隐藏 +
+
+

建议 Skill:revenue-reconciliation

+ +

目的

+

帮助 Codex 对 Stripe、数据仓库发票和人工调整表中的月度收入记录进行对账。

+ +

建议包结构

+
revenue-reconciliation/
+  SKILL.md
+  agents/openai.yaml
+  scripts/
+    reconcile_revenue.py
+  references/
+    csv-schema.md
+ +

SKILL.md 草稿

+
---
+name: revenue-reconciliation
+description: 对 Stripe balance export、warehouse invoice CSV 和调整表中的月度收入数据进行对账。当用户要求比较收入文件、找出不一致项或准备月结摘要时使用。
+---
+
+# 收入对账
+
+## 工作流
+1. 确认用户提供了 Stripe 导出、warehouse invoice CSV 和调整表。
+2. 阅读 `references/csv-schema.md`,了解预期列。
+3. 使用用户提供的文件运行 `scripts/reconcile_revenue.py`。
+4. 审查不一致项,并总结可能原因。
+5. 不要编造缺失值。清楚标记未解决记录。
+
+## 输出
+- 已检查文件
+- 匹配总额
+- 不一致项
+- 可能原因
+- 未解决项目
+- 月结摘要
+ +

脚本

+

`scripts/reconcile_revenue.py` 应解析 CSV 输入,规范化 invoice ID 和币种,比较金额,并输出 JSON 供 Codex 总结。

+ +

agents/openai.yaml

+
display_name: 收入对账
+short_description: 比对收入 CSV,并总结月结差异。
+default_prompt: 请对这些收入文件进行对账,并生成月结摘要。
+ +

验证

+

使用包含匹配发票、缺失发票、重复发票和仅调整项记录的样例 CSV。确认未解决差异不会被隐藏。

+
+
+ +
+ +
+

你的判断

+

只根据评审标准和页面里可见的两个输出做判断。选择会保存到当前浏览器的本地存储里,也可以回到入口页导出。

+
+ + + +
+ +
+ +
+ + +
+ + +
+

+
+
+ + + diff --git a/reports/blind-human-review-2026-06-29/case-03-webinar-repurposing.html b/reports/blind-human-review-2026-06-29/case-03-webinar-repurposing.html new file mode 100644 index 0000000..4ef048c --- /dev/null +++ b/reports/blind-human-review-2026-06-29/case-03-webinar-repurposing.html @@ -0,0 +1,356 @@ + + + + + + Webinar 内容复用 Skill - Skill 盲测评审 + + + +
+ +

Webinar 内容复用 Skill

+

A/B 盲测评审。记录完判断前,不要打开答案钥匙。

+
+
+
+

场景

+

一个市场团队经常把 webinar 逐字稿改写成 LinkedIn 帖子、邮件 teaser 和 newsletter 段落,同时必须保证所有说法都有来源,并避开禁止宣传的 claim。

+

用户请求

+

请做一个可复用 skill,把 webinar 逐字稿复用成市场内容。输入包括逐字稿、讲者姓名、产品定位说明和禁止使用的 claim。输出包括 LinkedIn 帖子、邮件 teaser、newsletter 段落、可引用金句和来源依据说明。

+

评审标准

+
  1. 是否保留来源约束,并处理 banned claims。
  2. +
  3. 是否定义可重复的多渠道输出格式。
  4. +
  5. 是否把语气和定位资料放到 references,避免入口膨胀。
  6. +
  7. 是否有针对幻觉 claim 和泛泛市场文案的评测。
  8. +
  9. 是否在中等治理风险下保持 skill 足够轻量。
+
+ +
+ +
+
+

方案 A

+ 来源已隐藏 +
+
+

建议 Skill:webinar-repurposer

+ +

目的

+

创建一个 skill,把 webinar 逐字稿转成可复用的市场内容。

+ +

包结构

+
webinar-repurposer/
+  SKILL.md
+  agents/openai.yaml
+  references/
+    brand-voice.md
+    banned-claims.md
+ +

SKILL.md 草稿

+
---
+name: webinar-repurposer
+description: 将 webinar 逐字稿复用成 LinkedIn 帖子、邮件 teaser、newsletter 段落、可引用金句和来源依据说明。当用户提供逐字稿、讲者、定位说明或禁止 claim 时使用。
+---
+
+# Webinar 内容复用
+
+## 工作流
+1. 阅读逐字稿、讲者姓名、产品定位说明和禁止 claim。
+2. 提取核心主题、最强的来源支持观点和可用引语。
+3. 起草一条 LinkedIn 帖子、一段邮件 teaser、一段 newsletter 和若干金句。
+4. 对照逐字稿和禁止 claim 检查每个说法。
+5. 为重要 claim 添加来源依据说明。
+
+## 输出
+- LinkedIn 帖子
+- 邮件 teaser
+- Newsletter 段落
+- 可引用金句
+- 来源依据说明
+- 已避免的 claim
+
+## 参考资料
+语气见 `references/brand-voice.md`,禁止 claim 见 `references/banned-claims.md`。
+ +

agents/openai.yaml

+
display_name: Webinar 内容复用
+short_description: 将 webinar 逐字稿转成有来源依据的市场内容。
+default_prompt: 请把这份 webinar 逐字稿复用成可发布的多渠道市场内容。
+ +

验证

+

用包含强金句、证据较弱、以及容易误用禁止 claim 的逐字稿测试。确认输出会避免没有依据的 claim。

+
+
+ + +
+
+

方案 B

+ 来源已隐藏 +
+
+

Skill 包建议:webinar-repurposer

+ +

边界

+

负责在提供来源逐字稿和定位约束时,把逐字稿复用为市场内容。排除全新 campaign strategy、没有依据的产品 claim、法律审核和一次性文案润色。

+ +

类型

+

Production-light。它需要输出质量检查和 banned-claim 防护,但除非涉及受监管 claim 或上市公司披露规则,否则不需要做成很重的 governed 包。

+ +

包结构

+
webinar-repurposer/
+  SKILL.md
+  agents/interface.yaml
+  references/
+    brand-voice.md
+    positioning-notes.md
+    banned-claims.md
+  evals/
+    output/
+      cases.jsonl
+  reports/
+    output-risk-profile.md
+    prompt-quality-profile.md
+ +

SKILL.md

+
---
+name: webinar-repurposer
+description: 将重复出现的 webinar 逐字稿复用为有来源依据的 LinkedIn 帖子、邮件 teaser、newsletter 段落、金句和 claim 依据说明。当提供逐字稿、讲者、定位说明或禁止 claim 时使用。不要用于无依据的发布 claim、法律审批、全新 campaign strategy 或一次性文案润色。
+---
+
+# Webinar 内容复用
+
+## 工作流
+1. 确认逐字稿是事实来源。
+2. 提取有来源支持的主题、讲者背书的引语和可安全复用的 claim。
+3. 起草前先检查 `references/banned-claims.md`。
+4. 生成各渠道输出,但不要添加逐字稿中没有的性能数字、客户结果或产品承诺。
+5. 添加来源依据说明,把每个主要 claim 绑定到逐字稿证据。
+
+## 必填输出
+- linkedin_post
+- email_teaser
+- newsletter_section
+- pull_quotes
+- source_grounding_notes
+- claims_avoided
+- missing_evidence
+ +

输出风险画像

+

常见失败:

+
    +
  • 生成任何产品都能用的泛泛市场话术。
  • +
  • 编造指标或客户结果。
  • +
  • 过度改写金句,导致意思偏离。
  • +
  • 因为某个 claim 很有吸引力而忽略 banned claims。
  • +
+ +

输出评测用例

+

1. 逐字稿包含诱人的“guaranteed ROI”禁止 claim:输出必须避开。

+

2. 逐字稿没有量化证明:输出不能编造数字。

+

3. 讲者原话不够顺:可以轻微修语法,但必须保留原意。

+

4. 近邻请求:“从零写一个新 campaign idea”不应路由到这里。

+ +

审查门禁

+
    +
  • 必填 section 必须全部出现。
  • +
  • Banned claim 检查必须显式出现。
  • +
  • 来源依据说明必须引用逐字稿位置或原话片段。
  • +
  • 缺失证据必须被标出,而不是用通用卖点填补。
  • +
+
+
+ +
+ +
+

你的判断

+

只根据评审标准和页面里可见的两个输出做判断。选择会保存到当前浏览器的本地存储里,也可以回到入口页导出。

+
+ + + +
+ +
+ +
+ + +
+ + +
+

+
+
+ + + diff --git a/reports/blind-human-review-2026-06-29/case-04-incident-postmortem.html b/reports/blind-human-review-2026-06-29/case-04-incident-postmortem.html new file mode 100644 index 0000000..f9833a3 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/case-04-incident-postmortem.html @@ -0,0 +1,361 @@ + + + + + + 故障复盘 Skill - Skill 盲测评审 + + + +
+ +

故障复盘 Skill

+

A/B 盲测评审。记录完判断前,不要打开答案钥匙。

+
+
+
+

场景

+

一个基础设施团队经常把 Slack 故障时间线、告警数据和部署记录整理成故障复盘和后续行动项。

+

用户请求

+

请创建一个可复用的 governed skill,用于故障复盘。输入包括告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表。输出应包含时间线、影响、根因假设、行动项、未解决证据和后续负责人矩阵。

+

评审标准

+
  1. 是否处理 governed/运营风险和未解决证据。
  2. +
  3. 是否区分假设和已确认根因。
  4. +
  5. 是否定义行动项负责人矩阵和回滚/跟进边界。
  6. +
  7. 是否要求 file-backed 或 source-backed 证据。
  8. +
  9. 是否有针对 blame language 和编造因果的审查门禁。
+
+ +
+ +
+
+

方案 A

+ 来源已隐藏 +
+
+

Skill 包建议:incident-postmortem

+ +

边界

+

负责根据 source-backed 时间线、Slack 摘要、部署记录、客户影响说明和负责人列表起草 governed 故障复盘。不要用于实时 incident command、最终 RCA 签核、HR/归责分析或面向客户的法律声明。

+ +

类型

+

Governed。这个包会影响运营责任归属,如果证据不足,容易制造虚假因果。

+ +

包结构

+
incident-postmortem/
+  SKILL.md
+  agents/interface.yaml
+  references/
+    postmortem-contract.md
+    severity-policy.md
+    blame-free-language.md
+    action-owner-matrix.md
+  evals/
+    output/
+      cases.jsonl
+      fixtures/
+        alert_timeline.json
+        slack_summary.md
+        deploy_notes.md
+  reports/
+    output-risk-profile.md
+    output_quality_scorecard.md
+    trust_report.md
+    review-studio.html
+ +

SKILL.md

+
---
+name: incident-postmortem
+description: 根据告警时间线、Slack 摘要、部署记录、影响说明和负责人列表起草 governed 故障复盘。用于 source-backed 时间线重建、影响总结、根因假设、行动项、未解决证据和后续负责人矩阵。不要用于实时故障指挥、最终 RCA 审批、归责或法律/客户承诺。
+---
+
+# 故障复盘
+
+## 工作流
+1. 要求提供 source-backed 故障输入,并把缺失输入列为 `missing evidence`。
+2. 只根据已提供时间戳建立时间线;如需推断顺序,必须明确标注。
+3. 分开列出 `confirmed_facts`、`root_cause_hypotheses` 和 `unresolved_evidence`。
+4. 总结客户影响,但不要编造受影响账户、持续时间或 SLA 违约。
+5. 创建行动项:包含 owner、due date、验证信号和回滚/跟进边界。
+6. 使用 `references/blame-free-language.md` 的无责语言。
+7. 除非 reviewer 明确提供,否则最终 RCA 审批不属于本 skill。
+
+## 输出契约
+- incident_summary
+- source_inputs
+- timeline
+- customer_impact
+- confirmed_facts
+- root_cause_hypotheses
+- unresolved_evidence
+- action_items
+- owner_matrix
+- approval_status
+ +

输出评测用例

+

1. Slack 和部署记录冲突:必须保留为未解决证据。

+

2. 告警时间线缺恢复时间:不能编造持续时长。

+

3. 用户要求实时故障决策:应路由出去,或说明本 skill 只做复盘。

+

4. 来源材料带有归责语言:输出必须改写为系统/流程语言。

+

5. 缺负责人列表:行动项 owner 字段应为 `missing evidence`。

+ +

门禁

+
    +
  • 时间线和部署记录需要 file-backed fixture 覆盖。
  • +
  • 输出评测禁止编造因果和隐藏未解决证据。
  • +
  • Review Studio 在人工 reviewer 审批行动负责人矩阵前保持 warning。
  • +
  • Trust report 应显示无网络和无破坏性操作。
  • +
+
+
+ + +
+
+

方案 B

+ 来源已隐藏 +
+
+

建议 Skill:incident-postmortem

+ +

目的

+

帮助 Codex 根据时间线、Slack 摘要、部署记录、影响说明和负责人列表创建结构化故障复盘。

+ +

包结构

+
incident-postmortem/
+  SKILL.md
+  agents/openai.yaml
+  references/
+    postmortem-template.md
+    severity-policy.md
+ +

SKILL.md 草稿

+
---
+name: incident-postmortem
+description: 根据告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表创建故障复盘。当用户需要时间线、影响、根因假设、行动项、未解决证据或负责人矩阵时使用。
+---
+
+# 故障复盘
+
+## 工作流
+1. 收集告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表。
+2. 建立按时间排序的时间线。
+3. 总结客户和系统影响。
+4. 识别已确认事实和根因假设。
+5. 在提供负责人和截止时间时,起草行动项。
+6. 清楚标记未解决证据。
+7. 避免归责式语言。
+
+## 输出
+- 故障摘要
+- 时间线
+- 影响
+- 根因假设
+- 已确认事实
+- 未解决证据
+- 行动项
+- 负责人矩阵
+ +

agents/openai.yaml

+
display_name: 故障复盘
+short_description: 根据故障证据生成结构化复盘。
+default_prompt: 请把这些故障证据整理成复盘和负责人矩阵。
+ +

验证

+

用证据完整、缺部署记录、Slack 说法冲突和暂时没有根因的 case 测试。确认 skill 会区分事实和假设。

+
+
+ +
+ +
+

你的判断

+

只根据评审标准和页面里可见的两个输出做判断。选择会保存到当前浏览器的本地存储里,也可以回到入口页导出。

+
+ + + +
+ +
+ +
+ + +
+ + +
+

+
+
+ + + diff --git a/reports/blind-human-review-2026-06-29/case-05-pr-review-followup.html b/reports/blind-human-review-2026-06-29/case-05-pr-review-followup.html new file mode 100644 index 0000000..0ed6348 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/case-05-pr-review-followup.html @@ -0,0 +1,344 @@ + + + + + + PR Review 跟进 Skill - Skill 盲测评审 + + + +
+ +

PR Review 跟进 Skill

+

A/B 盲测评审。记录完判断前,不要打开答案钥匙。

+
+
+
+

场景

+

一个小工程团队经常收到 GitHub PR review comments,希望 Codex 能把评论转成补丁、回复摘要和验证记录。

+

用户请求

+

请设计一个用于处理 PR review comments 的可复用 skill。输入包括 review comments、变更文件、失败 CI 说明和 maintainer preferences。输出需要分组评论、只修改相关文件、写回复说明并运行验证。必须避免回滚无关的用户改动。

+

评审标准

+
  1. 是否理解 code-review 跟进和 dirty worktree 安全。
  2. +
  3. 是否定义评论分组、补丁、回复和验证工作流。
  4. +
  5. 是否避免过度重构和无关 revert。
  6. +
  7. 是否说明变更后需要的命令和证据。
  8. +
  9. 是否定义一般 code review 与评论跟进的触发排除。
+
+ +
+ +
+
+

方案 A

+ 来源已隐藏 +
+
+

建议 Skill:pr-review-followup

+ +

目的

+

创建一个 skill,帮助 Codex 安全处理 PR review comments,并总结回复。

+ +

包结构

+
pr-review-followup/
+  SKILL.md
+  agents/openai.yaml
+  references/
+    response-style.md
+ +

SKILL.md 草稿

+
---
+name: pr-review-followup
+description: 通过分组反馈、修改相关文件、编写回复说明和运行验证来处理 GitHub PR review comments。当用户提供 review comments、变更文件、CI 说明或 maintainer preferences 时使用。
+---
+
+# PR Review 跟进
+
+## 工作流
+1. 阅读所有 review comments,并按文件、主题和严重程度分组。
+2. 编辑前检查受影响文件。
+3. 只修改处理 comments 所需的文件。
+4. 不要回滚无关用户改动。
+5. 运行相关测试或 CI 复现命令。
+6. 总结变更,并为 reviewer 起草回复说明。
+
+## 输出
+- 评论分组
+- 修改文件
+- 验证命令
+- Reviewer 回复说明
+- 剩余问题
+ +

agents/openai.yaml

+
display_name: PR Review 跟进
+short_description: 处理 PR comments 并准备 reviewer 回复。
+default_prompt: 请处理这些 PR review comments,并总结验证结果。
+ +

验证

+

用需要代码修改、文档修改、无需修改只需解释、以及 reviewer 要求冲突的 comments 测试。

+
+
+ + +
+
+

方案 B

+ 来源已隐藏 +
+
+

Skill 包建议:pr-review-followup

+ +

边界

+

负责 PR 已经有明确 reviewer comments、变更文件或 CI 说明之后的评论跟进。排除首次 code review、大范围重构、issue triage,以及没有绑定评论的推测性清理。

+ +

包结构

+
pr-review-followup/
+  SKILL.md
+  agents/interface.yaml
+  references/
+    response-style.md
+    dirty-worktree-safety.md
+    verification-policy.md
+  evals/
+    trigger_cases.json
+    output/
+      cases.jsonl
+  reports/
+    output-risk-profile.md
+    output_quality_scorecard.md
+ +

SKILL.md

+
---
+name: pr-review-followup
+description: 处理具体 PR review comments:分组反馈、只修改相关文件、保留无关用户改动、起草 reviewer 回复并运行验证。当提供 review comments、变更文件、失败 CI 说明或 maintainer preferences 时使用。不要用于首次 code review、大范围重构、issue triage 或无关清理。
+---
+
+# PR Review 跟进
+
+## 工作流
+1. 编辑前记录 git status,并识别用户已经改动的文件。
+2. 按文件、行为、严重程度,以及是否需要代码、测试、文档或仅解释来分组 review comments。
+3. 修改前阅读受影响代码和附近测试。
+4. 只修改与评论绑定的文件。不要回滚无关改动,也不要格式化未触碰文件。
+5. 运行有针对性的验证,并记录命令和结果。
+6. 起草 reviewer 回复说明,把每组评论映射到变更、无需修改的理由或剩余问题。
+7. 如果评论冲突,停止并询问哪个 reviewer 指令优先。
+
+## 输出契约
+- comment_groups
+- patch_summary
+- files_changed
+- verification
+- reviewer_response_notes
+- unresolved_questions
+- unrelated_changes_preserved
+ +

输出评测用例

+

1. 评论要求添加特定 null check:输出必须只修改受影响函数,并提到测试。

+

2. Reviewer 要求与 PR 范围无关的大重构:输出应标出范围风险。

+

3. Dirty worktree 中有无关用户文件:输出必须保留它。

+

4. 两个 reviewer comments 冲突:输出必须询问优先级,而不是猜。

+

5. 近邻请求:“从零 review 这个 PR”不应触发 follow-up mode。

+ +

门禁

+
    +
  • 触发评测必须区分首次 review 和评论跟进。
  • +
  • 如果回滚或隐藏无关改动,输出评测必须失败。
  • +
  • 验证策略必须记录跳过测试的原因。
  • +
  • 回复说明必须能追溯到评论分组。
  • +
+
+
+ +
+ +
+

你的判断

+

只根据评审标准和页面里可见的两个输出做判断。选择会保存到当前浏览器的本地存储里,也可以回到入口页导出。

+
+ + + +
+ +
+ +
+ + +
+ + +
+

+
+
+ + + diff --git a/reports/blind-human-review-2026-06-29/generate_pack.py b/reports/blind-human-review-2026-06-29/generate_pack.py new file mode 100644 index 0000000..ec75980 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/generate_pack.py @@ -0,0 +1,1245 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import html +import json +from pathlib import Path + + +OUT_DIR = Path(__file__).resolve().parent +YAO_SKILL_PATH = "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill/SKILL.md" +OFFICIAL_SKILL_PATH = "/Users/laoyao/.codex/skills/.system/skill-creator/SKILL.md" +SEED = "blind-human-review-2026-06-29-v1" + + +CASES = [ + { + "id": "case-01-support-triage", + "title": "客服工单分诊 Skill", + "scenario": "一个 B2B SaaS 团队每天都要处理客户工单:判断分类、紧急程度、起草首轮回复,并把账务、安全、故障类问题升级给对应负责人。他们希望把这个重复流程做成一个给支持团队使用的 Codex skill。", + "user_prompt": "请把我们反复使用的客服工单分诊流程做成一个团队可复用的 skill。输入包括原始工单文本、客户等级、产品模块和近期故障记录。输出需要包含问题分类、紧急程度、首轮回复草稿、升级负责人和判断依据。不要把 PII 泄露到报告里。", + "rubric": [ + "是否有清楚的触发边界和近邻排除。", + "是否是可复用的 skill 包结构,而不只是一个提示词。", + "是否定义了分类、紧急程度、回复、升级和证据的输出契约。", + "是否处理 PII 和客户内容的隐私风险。", + "是否有能发现错误升级和泛泛回复的评测方案。", + ], + "order": {"A": "official", "B": "yao"}, + "official": """## 建议 Skill:support-triage + +### 目的 +创建一个简洁的 skill,帮助 Codex 对 B2B SaaS 客服工单进行分诊,并起草首轮回复。 + +### 建议包结构 +``` +support-triage/ + SKILL.md + agents/openai.yaml + references/ + triage-taxonomy.md + escalation-policy.md +``` + +### SKILL.md 草稿 +```markdown +--- +name: support-triage +description: 对 B2B SaaS 客服工单进行分诊并起草首轮回复。当用户提供原始工单文本、客户等级、产品模块、故障记录,或需要紧急程度、分类、升级建议时使用。 +--- + +# 客服工单分诊 + +使用这个 skill 对入站工单分类、起草合适的首轮回复,并识别是否需要升级。 + +## 工作流 +1. 阅读工单文本、客户等级、产品模块和近期故障记录。 +2. 将工单归为一个主分类:账务、访问权限、缺陷、故障、功能请求、安全、数据/隐私、上手问题或其他。 +3. 标记紧急程度:低、普通、高、严重。 +4. 起草首轮回复:确认问题、只询问必要补充信息,并避免没有依据的承诺。 +5. 对安全、隐私、严重故障、数据丢失或企业客户影响问题进行升级。 +6. 汇报判断依据,但不要复制不必要的 PII。 + +## 输出格式 +- 分类: +- 紧急程度: +- 首轮回复: +- 升级负责人: +- 判断依据: +- 缺失信息: + +## 参考资料 +- 分类规则见 `references/triage-taxonomy.md`。 +- 升级标准见 `references/escalation-policy.md`。 +``` + +### agents/openai.yaml +```yaml +display_name: 客服工单分诊 +short_description: 对客服工单分类,并起草首轮回复。 +default_prompt: 请分诊这个客服工单,并起草首轮回复。 +``` + +### 资源 +- `references/triage-taxonomy.md`:分类定义、紧急程度提示和示例。 +- `references/escalation-policy.md`:升级负责人和升级标准。 + +### 验证 +用账务、安全、故障和功能请求等代表性工单测试。确认输出会最小化 PII,并且严重问题会被升级。 +""", + "yao": """## Skill 包建议:support-triage + +### 边界 +负责重复出现的客服工单分诊:用户提供工单文本以及客户/产品上下文,需要结构化路由决策。不要用于一次性文案润色、泛泛的客服同理心训练、CRM 配置,或超出升级范围的法律/安全事件处置。 + +### 输出契约 +这个 skill 必须返回: +- `category` +- `urgency` +- `first_response_draft` +- `escalation_owner` +- `evidence_used` +- `missing_information` +- `pii_handling_note` + +### 包结构 +``` +support-triage/ + SKILL.md + agents/interface.yaml + references/ + triage-taxonomy.md + escalation-policy.md + privacy-redaction.md + evals/ + trigger_cases.json + output/ + cases.jsonl + fixtures/ + reports/ + output-risk-profile.md + output_quality_scorecard.md +``` + +### SKILL.md +```markdown +--- +name: support-triage +description: 将重复出现的 B2B SaaS 客服工单分诊为分类、紧急程度、首轮回复、升级负责人、判断依据和缺失信息。当输入包含原始工单、客户等级、产品模块、故障记录或支持队列上下文时使用。不要用于一次性文案润色、CRM 设置、法律建议或最终安全事件处置。 +--- + +# 客服工单分诊 + +## 工作流 +1. 确认这是重复的工单分诊任务,而不是一次性文案润色。 +2. 阅读工单文本、客户等级、产品模块和故障记录。 +3. 使用 `references/triage-taxonomy.md` 判断分类和紧急程度。 +4. 按 `references/escalation-policy.md` 检查升级标准。 +5. 起草首轮回复:确认问题、避免无依据承诺,只请求最少的缺失信息。 +6. 按 `references/privacy-redaction.md` 对 PII 做脱敏或摘要。 +7. 严格按输出契约返回。 + +## 必填输出 +使用这些字段: +- category +- urgency +- first_response_draft +- escalation_owner +- evidence_used +- missing_information +- pii_handling_note +``` + +### agents/interface.yaml +```yaml +display_name: 客服工单分诊 +short_description: 对工单分类、起草首轮回复,并给出升级依据。 +default_prompt: 请用分类、紧急程度、回复、升级负责人和依据来分诊这个客服工单。 +``` + +### 评测用例 +1. 企业客户在已知故障期间提交工单:必须标为 `critical`,并升级给 incident commander。 +2. SMB 客户的账务困惑:不应过度升级。 +3. 带客户邮箱和 token 片段的安全披露:必须脱敏 token,并升级安全团队。 +4. 伪装成投诉的功能请求:应归类为功能请求,紧急程度为普通。 +5. 近邻请求:“把这段回复改得更温和”不应触发包级分诊。 + +### 发布门禁 +- 触发评测:正例、反例、近邻文案润色。 +- 输出评测:首轮回复不能编造 SLA,升级必须匹配策略。 +- 隐私检查:报告不能保存原始工单 PII。 +- 审查备注:如果分类规则或升级负责人未提供,标记为 `missing evidence`,不要编造。 + +### 下一步 +加入 3 个匿名工单 fixture,运行输出评测,并记录错误升级、泛泛回复和 PII 泄露的失败分类。 +""", + }, + { + "id": "case-02-monthly-revenue-reconciliation", + "title": "月度收入对账 Skill", + "scenario": "一个财务运营团队每个月结账前都要比对 Stripe 导出、数据仓库发票 CSV 和人工调整表,找出金额、币种、发票和调整项的不一致。", + "user_prompt": "请创建一个用于月度收入对账的可复用 skill。输入是 Stripe balance export CSV、warehouse invoice CSV 和 adjustments sheet。输出需要标出不一致项、解释可能原因,并生成可用于月结的摘要。准确性比速度更重要。", + "rubric": [ + "是否识别出这是 file-backed 且准确性敏感的任务。", + "是否定义了确定性脚本和资源边界。", + "是否说明输入/输出 schema 与对账容差。", + "是否包含 fixture 和失败用例验证。", + "是否避免编造财务事实或隐藏未解决差异。", + ], + "order": {"A": "yao", "B": "official"}, + "official": """## 建议 Skill:revenue-reconciliation + +### 目的 +帮助 Codex 对 Stripe、数据仓库发票和人工调整表中的月度收入记录进行对账。 + +### 建议包结构 +``` +revenue-reconciliation/ + SKILL.md + agents/openai.yaml + scripts/ + reconcile_revenue.py + references/ + csv-schema.md +``` + +### SKILL.md 草稿 +```markdown +--- +name: revenue-reconciliation +description: 对 Stripe balance export、warehouse invoice CSV 和调整表中的月度收入数据进行对账。当用户要求比较收入文件、找出不一致项或准备月结摘要时使用。 +--- + +# 收入对账 + +## 工作流 +1. 确认用户提供了 Stripe 导出、warehouse invoice CSV 和调整表。 +2. 阅读 `references/csv-schema.md`,了解预期列。 +3. 使用用户提供的文件运行 `scripts/reconcile_revenue.py`。 +4. 审查不一致项,并总结可能原因。 +5. 不要编造缺失值。清楚标记未解决记录。 + +## 输出 +- 已检查文件 +- 匹配总额 +- 不一致项 +- 可能原因 +- 未解决项目 +- 月结摘要 +``` + +### 脚本 +`scripts/reconcile_revenue.py` 应解析 CSV 输入,规范化 invoice ID 和币种,比较金额,并输出 JSON 供 Codex 总结。 + +### agents/openai.yaml +```yaml +display_name: 收入对账 +short_description: 比对收入 CSV,并总结月结差异。 +default_prompt: 请对这些收入文件进行对账,并生成月结摘要。 +``` + +### 验证 +使用包含匹配发票、缺失发票、重复发票和仅调整项记录的样例 CSV。确认未解决差异不会被隐藏。 +""", + "yao": """## Skill 包建议:revenue-reconciliation + +### 边界 +负责围绕 Stripe balance export、warehouse invoice CSV 和调整表的重复月结对账。排除税务建议、收入确认政策决策、支付渠道配置和一次性表格美化。 + +### 风险等级 +Governed,file-backed,准确性敏感。任何可用于月结的结论前,都必须有确定性解析和可见的未解决项目。 + +### 包结构 +``` +revenue-reconciliation/ + SKILL.md + agents/interface.yaml + scripts/ + reconcile_revenue.py + validate_reconciliation_fixture.py + references/ + input-schema.md + tolerance-policy.md + close-summary-contract.md + evals/ + output/ + cases.jsonl + fixtures/ + stripe_sample.csv + warehouse_sample.csv + adjustments_sample.csv + reports/ + output-risk-profile.md + output_quality_scorecard.md + trust_report.md +``` + +### SKILL.md +```markdown +--- +name: revenue-reconciliation +description: 对来自 Stripe 导出、warehouse invoice CSV 和调整表的重复月结收入文件进行对账。用于 file-backed 差异检测、可能原因分析、未解决项目报告和月结摘要。不要用于收入确认建议、税务指导、支付渠道配置或一次性表格格式整理。 +--- + +# 收入对账 + +## 工作流 +1. 要求提供所有输入文件;缺失文件必须标记为 `missing evidence`。 +2. 阅读 `references/input-schema.md`,先校验列再分析。 +3. 运行 `scripts/reconcile_revenue.py`,规范化 ID、币种、金额精度、日期和调整项关联。 +4. 审查 JSON 输出,并按缺失发票、金额差异、币种差异、重复项、时间差异或仅调整项分类。 +5. 只基于脚本证据生成月结摘要。 +6. 保持未解决差异可见。不要编造原因、审批或政策判断。 + +## 输出契约 +- input_files +- validation_status +- totals_by_source +- matched_count +- mismatch_table +- likely_causes +- unresolved_items +- close_ready_summary +- missing_evidence +``` + +### 确定性脚本契约 +`scripts/reconcile_revenue.py` 应接受: +```bash +python3 scripts/reconcile_revenue.py --stripe stripe.csv --warehouse warehouse.csv --adjustments adjustments.csv --output reconciliation.json +``` + +它应返回包含源行 ID 的结构化 JSON,让摘要里的每个结论都能追溯到 file-backed fixture。 + +### 评测 +- Fixture 1:所有记录匹配。 +- Fixture 2:Stripe 有付款,但 warehouse 缺发票。 +- Fixture 3:warehouse 金额超过容差。 +- Fixture 4:人工调整解释了差异。 +- 近邻请求:“把这个表格弄好看一点”不应路由到这里。 + +### 门禁 +- 汇总前必须通过输入 schema 校验。 +- 如果遗漏未解决差异,输出评测必须失败。 +- Trust check 应标记网络访问为不必要。 +- Review Studio 应在晋级前显示 file-backed fixture 覆盖和未解决差异处理。 +""", + }, + { + "id": "case-03-webinar-repurposing", + "title": "Webinar 内容复用 Skill", + "scenario": "一个市场团队经常把 webinar 逐字稿改写成 LinkedIn 帖子、邮件 teaser 和 newsletter 段落,同时必须保证所有说法都有来源,并避开禁止宣传的 claim。", + "user_prompt": "请做一个可复用 skill,把 webinar 逐字稿复用成市场内容。输入包括逐字稿、讲者姓名、产品定位说明和禁止使用的 claim。输出包括 LinkedIn 帖子、邮件 teaser、newsletter 段落、可引用金句和来源依据说明。", + "rubric": [ + "是否保留来源约束,并处理 banned claims。", + "是否定义可重复的多渠道输出格式。", + "是否把语气和定位资料放到 references,避免入口膨胀。", + "是否有针对幻觉 claim 和泛泛市场文案的评测。", + "是否在中等治理风险下保持 skill 足够轻量。", + ], + "order": {"A": "official", "B": "yao"}, + "official": """## 建议 Skill:webinar-repurposer + +### 目的 +创建一个 skill,把 webinar 逐字稿转成可复用的市场内容。 + +### 包结构 +``` +webinar-repurposer/ + SKILL.md + agents/openai.yaml + references/ + brand-voice.md + banned-claims.md +``` + +### SKILL.md 草稿 +```markdown +--- +name: webinar-repurposer +description: 将 webinar 逐字稿复用成 LinkedIn 帖子、邮件 teaser、newsletter 段落、可引用金句和来源依据说明。当用户提供逐字稿、讲者、定位说明或禁止 claim 时使用。 +--- + +# Webinar 内容复用 + +## 工作流 +1. 阅读逐字稿、讲者姓名、产品定位说明和禁止 claim。 +2. 提取核心主题、最强的来源支持观点和可用引语。 +3. 起草一条 LinkedIn 帖子、一段邮件 teaser、一段 newsletter 和若干金句。 +4. 对照逐字稿和禁止 claim 检查每个说法。 +5. 为重要 claim 添加来源依据说明。 + +## 输出 +- LinkedIn 帖子 +- 邮件 teaser +- Newsletter 段落 +- 可引用金句 +- 来源依据说明 +- 已避免的 claim + +## 参考资料 +语气见 `references/brand-voice.md`,禁止 claim 见 `references/banned-claims.md`。 +``` + +### agents/openai.yaml +```yaml +display_name: Webinar 内容复用 +short_description: 将 webinar 逐字稿转成有来源依据的市场内容。 +default_prompt: 请把这份 webinar 逐字稿复用成可发布的多渠道市场内容。 +``` + +### 验证 +用包含强金句、证据较弱、以及容易误用禁止 claim 的逐字稿测试。确认输出会避免没有依据的 claim。 +""", + "yao": """## Skill 包建议:webinar-repurposer + +### 边界 +负责在提供来源逐字稿和定位约束时,把逐字稿复用为市场内容。排除全新 campaign strategy、没有依据的产品 claim、法律审核和一次性文案润色。 + +### 类型 +Production-light。它需要输出质量检查和 banned-claim 防护,但除非涉及受监管 claim 或上市公司披露规则,否则不需要做成很重的 governed 包。 + +### 包结构 +``` +webinar-repurposer/ + SKILL.md + agents/interface.yaml + references/ + brand-voice.md + positioning-notes.md + banned-claims.md + evals/ + output/ + cases.jsonl + reports/ + output-risk-profile.md + prompt-quality-profile.md +``` + +### SKILL.md +```markdown +--- +name: webinar-repurposer +description: 将重复出现的 webinar 逐字稿复用为有来源依据的 LinkedIn 帖子、邮件 teaser、newsletter 段落、金句和 claim 依据说明。当提供逐字稿、讲者、定位说明或禁止 claim 时使用。不要用于无依据的发布 claim、法律审批、全新 campaign strategy 或一次性文案润色。 +--- + +# Webinar 内容复用 + +## 工作流 +1. 确认逐字稿是事实来源。 +2. 提取有来源支持的主题、讲者背书的引语和可安全复用的 claim。 +3. 起草前先检查 `references/banned-claims.md`。 +4. 生成各渠道输出,但不要添加逐字稿中没有的性能数字、客户结果或产品承诺。 +5. 添加来源依据说明,把每个主要 claim 绑定到逐字稿证据。 + +## 必填输出 +- linkedin_post +- email_teaser +- newsletter_section +- pull_quotes +- source_grounding_notes +- claims_avoided +- missing_evidence +``` + +### 输出风险画像 +常见失败: +- 生成任何产品都能用的泛泛市场话术。 +- 编造指标或客户结果。 +- 过度改写金句,导致意思偏离。 +- 因为某个 claim 很有吸引力而忽略 banned claims。 + +### 输出评测用例 +1. 逐字稿包含诱人的“guaranteed ROI”禁止 claim:输出必须避开。 +2. 逐字稿没有量化证明:输出不能编造数字。 +3. 讲者原话不够顺:可以轻微修语法,但必须保留原意。 +4. 近邻请求:“从零写一个新 campaign idea”不应路由到这里。 + +### 审查门禁 +- 必填 section 必须全部出现。 +- Banned claim 检查必须显式出现。 +- 来源依据说明必须引用逐字稿位置或原话片段。 +- 缺失证据必须被标出,而不是用通用卖点填补。 +""", + }, + { + "id": "case-04-incident-postmortem", + "title": "故障复盘 Skill", + "scenario": "一个基础设施团队经常把 Slack 故障时间线、告警数据和部署记录整理成故障复盘和后续行动项。", + "user_prompt": "请创建一个可复用的 governed skill,用于故障复盘。输入包括告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表。输出应包含时间线、影响、根因假设、行动项、未解决证据和后续负责人矩阵。", + "rubric": [ + "是否处理 governed/运营风险和未解决证据。", + "是否区分假设和已确认根因。", + "是否定义行动项负责人矩阵和回滚/跟进边界。", + "是否要求 file-backed 或 source-backed 证据。", + "是否有针对 blame language 和编造因果的审查门禁。", + ], + "order": {"A": "yao", "B": "official"}, + "official": """## 建议 Skill:incident-postmortem + +### 目的 +帮助 Codex 根据时间线、Slack 摘要、部署记录、影响说明和负责人列表创建结构化故障复盘。 + +### 包结构 +``` +incident-postmortem/ + SKILL.md + agents/openai.yaml + references/ + postmortem-template.md + severity-policy.md +``` + +### SKILL.md 草稿 +```markdown +--- +name: incident-postmortem +description: 根据告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表创建故障复盘。当用户需要时间线、影响、根因假设、行动项、未解决证据或负责人矩阵时使用。 +--- + +# 故障复盘 + +## 工作流 +1. 收集告警时间线、Slack 摘要、部署记录、客户影响说明和负责人列表。 +2. 建立按时间排序的时间线。 +3. 总结客户和系统影响。 +4. 识别已确认事实和根因假设。 +5. 在提供负责人和截止时间时,起草行动项。 +6. 清楚标记未解决证据。 +7. 避免归责式语言。 + +## 输出 +- 故障摘要 +- 时间线 +- 影响 +- 根因假设 +- 已确认事实 +- 未解决证据 +- 行动项 +- 负责人矩阵 +``` + +### agents/openai.yaml +```yaml +display_name: 故障复盘 +short_description: 根据故障证据生成结构化复盘。 +default_prompt: 请把这些故障证据整理成复盘和负责人矩阵。 +``` + +### 验证 +用证据完整、缺部署记录、Slack 说法冲突和暂时没有根因的 case 测试。确认 skill 会区分事实和假设。 +""", + "yao": """## Skill 包建议:incident-postmortem + +### 边界 +负责根据 source-backed 时间线、Slack 摘要、部署记录、客户影响说明和负责人列表起草 governed 故障复盘。不要用于实时 incident command、最终 RCA 签核、HR/归责分析或面向客户的法律声明。 + +### 类型 +Governed。这个包会影响运营责任归属,如果证据不足,容易制造虚假因果。 + +### 包结构 +``` +incident-postmortem/ + SKILL.md + agents/interface.yaml + references/ + postmortem-contract.md + severity-policy.md + blame-free-language.md + action-owner-matrix.md + evals/ + output/ + cases.jsonl + fixtures/ + alert_timeline.json + slack_summary.md + deploy_notes.md + reports/ + output-risk-profile.md + output_quality_scorecard.md + trust_report.md + review-studio.html +``` + +### SKILL.md +```markdown +--- +name: incident-postmortem +description: 根据告警时间线、Slack 摘要、部署记录、影响说明和负责人列表起草 governed 故障复盘。用于 source-backed 时间线重建、影响总结、根因假设、行动项、未解决证据和后续负责人矩阵。不要用于实时故障指挥、最终 RCA 审批、归责或法律/客户承诺。 +--- + +# 故障复盘 + +## 工作流 +1. 要求提供 source-backed 故障输入,并把缺失输入列为 `missing evidence`。 +2. 只根据已提供时间戳建立时间线;如需推断顺序,必须明确标注。 +3. 分开列出 `confirmed_facts`、`root_cause_hypotheses` 和 `unresolved_evidence`。 +4. 总结客户影响,但不要编造受影响账户、持续时间或 SLA 违约。 +5. 创建行动项:包含 owner、due date、验证信号和回滚/跟进边界。 +6. 使用 `references/blame-free-language.md` 的无责语言。 +7. 除非 reviewer 明确提供,否则最终 RCA 审批不属于本 skill。 + +## 输出契约 +- incident_summary +- source_inputs +- timeline +- customer_impact +- confirmed_facts +- root_cause_hypotheses +- unresolved_evidence +- action_items +- owner_matrix +- approval_status +``` + +### 输出评测用例 +1. Slack 和部署记录冲突:必须保留为未解决证据。 +2. 告警时间线缺恢复时间:不能编造持续时长。 +3. 用户要求实时故障决策:应路由出去,或说明本 skill 只做复盘。 +4. 来源材料带有归责语言:输出必须改写为系统/流程语言。 +5. 缺负责人列表:行动项 owner 字段应为 `missing evidence`。 + +### 门禁 +- 时间线和部署记录需要 file-backed fixture 覆盖。 +- 输出评测禁止编造因果和隐藏未解决证据。 +- Review Studio 在人工 reviewer 审批行动负责人矩阵前保持 warning。 +- Trust report 应显示无网络和无破坏性操作。 +""", + }, + { + "id": "case-05-pr-review-followup", + "title": "PR Review 跟进 Skill", + "scenario": "一个小工程团队经常收到 GitHub PR review comments,希望 Codex 能把评论转成补丁、回复摘要和验证记录。", + "user_prompt": "请设计一个用于处理 PR review comments 的可复用 skill。输入包括 review comments、变更文件、失败 CI 说明和 maintainer preferences。输出需要分组评论、只修改相关文件、写回复说明并运行验证。必须避免回滚无关的用户改动。", + "rubric": [ + "是否理解 code-review 跟进和 dirty worktree 安全。", + "是否定义评论分组、补丁、回复和验证工作流。", + "是否避免过度重构和无关 revert。", + "是否说明变更后需要的命令和证据。", + "是否定义一般 code review 与评论跟进的触发排除。", + ], + "order": {"A": "official", "B": "yao"}, + "official": """## 建议 Skill:pr-review-followup + +### 目的 +创建一个 skill,帮助 Codex 安全处理 PR review comments,并总结回复。 + +### 包结构 +``` +pr-review-followup/ + SKILL.md + agents/openai.yaml + references/ + response-style.md +``` + +### SKILL.md 草稿 +```markdown +--- +name: pr-review-followup +description: 通过分组反馈、修改相关文件、编写回复说明和运行验证来处理 GitHub PR review comments。当用户提供 review comments、变更文件、CI 说明或 maintainer preferences 时使用。 +--- + +# PR Review 跟进 + +## 工作流 +1. 阅读所有 review comments,并按文件、主题和严重程度分组。 +2. 编辑前检查受影响文件。 +3. 只修改处理 comments 所需的文件。 +4. 不要回滚无关用户改动。 +5. 运行相关测试或 CI 复现命令。 +6. 总结变更,并为 reviewer 起草回复说明。 + +## 输出 +- 评论分组 +- 修改文件 +- 验证命令 +- Reviewer 回复说明 +- 剩余问题 +``` + +### agents/openai.yaml +```yaml +display_name: PR Review 跟进 +short_description: 处理 PR comments 并准备 reviewer 回复。 +default_prompt: 请处理这些 PR review comments,并总结验证结果。 +``` + +### 验证 +用需要代码修改、文档修改、无需修改只需解释、以及 reviewer 要求冲突的 comments 测试。 +""", + "yao": """## Skill 包建议:pr-review-followup + +### 边界 +负责 PR 已经有明确 reviewer comments、变更文件或 CI 说明之后的评论跟进。排除首次 code review、大范围重构、issue triage,以及没有绑定评论的推测性清理。 + +### 包结构 +``` +pr-review-followup/ + SKILL.md + agents/interface.yaml + references/ + response-style.md + dirty-worktree-safety.md + verification-policy.md + evals/ + trigger_cases.json + output/ + cases.jsonl + reports/ + output-risk-profile.md + output_quality_scorecard.md +``` + +### SKILL.md +```markdown +--- +name: pr-review-followup +description: 处理具体 PR review comments:分组反馈、只修改相关文件、保留无关用户改动、起草 reviewer 回复并运行验证。当提供 review comments、变更文件、失败 CI 说明或 maintainer preferences 时使用。不要用于首次 code review、大范围重构、issue triage 或无关清理。 +--- + +# PR Review 跟进 + +## 工作流 +1. 编辑前记录 git status,并识别用户已经改动的文件。 +2. 按文件、行为、严重程度,以及是否需要代码、测试、文档或仅解释来分组 review comments。 +3. 修改前阅读受影响代码和附近测试。 +4. 只修改与评论绑定的文件。不要回滚无关改动,也不要格式化未触碰文件。 +5. 运行有针对性的验证,并记录命令和结果。 +6. 起草 reviewer 回复说明,把每组评论映射到变更、无需修改的理由或剩余问题。 +7. 如果评论冲突,停止并询问哪个 reviewer 指令优先。 + +## 输出契约 +- comment_groups +- patch_summary +- files_changed +- verification +- reviewer_response_notes +- unresolved_questions +- unrelated_changes_preserved +``` + +### 输出评测用例 +1. 评论要求添加特定 null check:输出必须只修改受影响函数,并提到测试。 +2. Reviewer 要求与 PR 范围无关的大重构:输出应标出范围风险。 +3. Dirty worktree 中有无关用户文件:输出必须保留它。 +4. 两个 reviewer comments 冲突:输出必须询问优先级,而不是猜。 +5. 近邻请求:“从零 review 这个 PR”不应触发 follow-up mode。 + +### 门禁 +- 触发评测必须区分首次 review 和评论跟进。 +- 如果回滚或隐藏无关改动,输出评测必须失败。 +- 验证策略必须记录跳过测试的原因。 +- 回复说明必须能追溯到评论分组。 +""", + }, +] + + +def esc(value: object) -> str: + return html.escape(str(value), quote=True) + + +def md_to_html(text: str) -> str: + lines = [] + in_code = False + in_list = False + code_lines = [] + def close_list() -> None: + nonlocal in_list + if in_list: + lines.append("") + in_list = False + for raw in text.splitlines(): + line = raw.rstrip() + if line.startswith("```"): + if in_code: + lines.append("
" + esc("\n".join(code_lines)) + "
") + code_lines = [] + in_code = False + else: + close_list() + in_code = True + continue + if in_code: + code_lines.append(raw) + continue + if not line: + close_list() + lines.append("") + elif line.startswith("### "): + close_list() + lines.append(f"

{esc(line[4:])}

") + elif line.startswith("## "): + close_list() + lines.append(f"

{esc(line[3:])}

") + elif line.startswith("# "): + close_list() + lines.append(f"

{esc(line[2:])}

") + elif line.startswith("- "): + if not in_list: + lines.append("
    ") + in_list = True + lines.append(f"
  • {esc(line[2:])}
  • ") + else: + close_list() + lines.append(f"

    {esc(line)}

    ") + if in_code: + lines.append("
    " + esc("\n".join(code_lines)) + "
    ") + close_list() + html_text = "\n".join(lines) + return html_text + + +def base_css() -> str: + return """ +:root { + color-scheme: light; + --bg: #f7f8fb; + --panel: #ffffff; + --ink: #20242c; + --muted: #687082; + --line: #d9deea; + --accent: #2563eb; + --accent-2: #0f766e; + --warn: #b45309; +} +* { box-sizing: border-box; } +body { + margin: 0; + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + background: var(--bg); + color: var(--ink); + line-height: 1.55; +} +header { + padding: 28px clamp(18px, 4vw, 52px) 18px; + background: #ffffff; + border-bottom: 1px solid var(--line); +} +main { padding: 24px clamp(18px, 4vw, 52px) 52px; } +h1 { margin: 0 0 8px; font-size: clamp(26px, 3vw, 38px); letter-spacing: 0; } +h2 { margin: 0 0 12px; font-size: 20px; } +h3 { margin: 18px 0 8px; font-size: 16px; } +p { margin: 0 0 10px; } +a { color: var(--accent); text-decoration: none; } +a:hover { text-decoration: underline; } +.meta, .note { color: var(--muted); } +.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; align-items: start; } +.card, .panel { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 8px; + padding: 18px; +} +.case-list { display: grid; gap: 12px; margin-top: 18px; } +.case-row { + display: grid; + grid-template-columns: 1fr auto; + gap: 12px; + align-items: center; + background: var(--panel); + border: 1px solid var(--line); + border-radius: 8px; + padding: 14px 16px; +} +.badge { + display: inline-flex; + align-items: center; + min-height: 28px; + padding: 4px 10px; + border-radius: 999px; + background: #e8f0ff; + color: #1d4ed8; + font-size: 13px; + font-weight: 650; +} +.rubric { margin: 0; padding-left: 20px; } +.rubric li { margin-bottom: 6px; } +.variant { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 8px; + overflow: hidden; +} +.variant-head { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: center; + padding: 12px 16px; + border-bottom: 1px solid var(--line); + background: #f2f5fa; +} +.variant-body { padding: 16px; } +pre { + overflow: auto; + padding: 12px; + border-radius: 6px; + background: #111827; + color: #f9fafb; + font-size: 13px; +} +code { font-family: "SFMono-Regular", Consolas, monospace; } +.review-box { + margin-top: 18px; + border: 1px solid var(--line); + border-radius: 8px; + padding: 16px; + background: #fff; +} +.controls { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0; } +button, .button { + border: 1px solid var(--line); + border-radius: 6px; + background: #fff; + color: var(--ink); + min-height: 36px; + padding: 7px 12px; + cursor: pointer; + font-weight: 650; +} +button.primary, .button.primary { background: var(--accent); border-color: var(--accent); color: #fff; } +textarea { + width: 100%; + min-height: 96px; + resize: vertical; + border: 1px solid var(--line); + border-radius: 6px; + padding: 10px; + font: inherit; +} +.warn { color: var(--warn); font-weight: 650; } +.split { display: flex; flex-wrap: wrap; gap: 10px; } +.small { font-size: 13px; } +@media (max-width: 900px) { + .grid { grid-template-columns: 1fr; } + .case-row { grid-template-columns: 1fr; } +} +""" + + +def case_filename(case: dict) -> str: + return f"{case['id']}.html" + + +def variant_html(case: dict, label: str) -> str: + source = case["order"][label] + content = case[source] + return f""" +
    +
    +

    方案 {label}

    + 来源已隐藏 +
    +
    + {md_to_html(content)} +
    +
    +""" + + +def render_case(case: dict, previous_link: str | None, next_link: str | None) -> str: + rubric_items = "\n".join(f"
  • {esc(item)}
  • " for item in case["rubric"]) + nav = ["返回入口"] + if previous_link: + nav.append(f"上一个") + if next_link: + nav.append(f"下一个") + return f""" + + + + + {esc(case['title'])} - Skill 盲测评审 + + + +
    +
    {''.join(nav)}
    +

    {esc(case['title'])}

    +

    A/B 盲测评审。记录完判断前,不要打开答案钥匙。

    +
    +
    +
    +

    场景

    +

    {esc(case['scenario'])}

    +

    用户请求

    +

    {esc(case['user_prompt'])}

    +

    评审标准

    +
      {rubric_items}
    +
    + +
    + {variant_html(case, "A")} + {variant_html(case, "B")} +
    + +
    +

    你的判断

    +

    只根据评审标准和页面里可见的两个输出做判断。选择会保存到当前浏览器的本地存储里,也可以回到入口页导出。

    +
    + + + +
    + +
    + +
    + + +
    + + +
    +

    +
    +
    + + + +""" + + +def render_index() -> str: + rows = [] + for index, case in enumerate(CASES, start=1): + rows.append( + f""" +
    +
    +
    案例 {index}
    +

    {esc(case['title'])}

    +

    {esc(case['scenario'])}

    +
    + 打开报告 +
    +""" + ) + return f""" + + + + + 人工盲测评审包 - Meta Skill 对比 + + + +
    +

    人工盲测评审包

    +

    5 个真实常见的 skill 创建场景。打开答案钥匙前,方案来源都会保持隐藏。

    +
    +
    +
    +

    评审说明

    +

    依次打开每个案例,按评审标准比较方案 A 和方案 B,保存胜出方案和理由,然后回到这里导出判断结果。5 个判断全部保存前,不要打开 DO_NOT_OPEN_answer_key.htmlDO_NOT_OPEN_answer_key.json

    +

    证据边界:这些输出是 Codex 在本地套用两份 skill 说明生成的,适合做人类盲测评审,但不能冒充 provider-backed 的独立模型执行证据。

    +
    + + 判断模板 + 盲测包 JSON +
    + +
    +
    + {''.join(rows)} +
    +
    + + + +""" + + +def render_answer_key() -> str: + rows = [] + source_labels = {"yao": "yao-meta-skill", "official": "官方 skill-creator"} + for case in CASES: + rows.append( + f"{esc(case['id'])}{esc(source_labels[case['order']['A']])}{esc(source_labels[case['order']['B']])}" + ) + return f""" + + + + + 答案钥匙 - Skill 盲测评审 + + + +
    + 返回入口 +

    答案钥匙

    +

    请只在所有评审判断都记录完成后再打开。

    +
    +
    +
    + + + {''.join(rows)} +
    案例方案 A 来源方案 B 来源
    +

    来源路径:yao-meta-skill = {esc(YAO_SKILL_PATH)};官方 skill-creator = {esc(OFFICIAL_SKILL_PATH)}。

    +
    +
    + + +""" + + +def write_json(path: Path, payload: object) -> None: + path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + +def write_html(path: Path, text: str) -> None: + normalized = "\n".join(line.rstrip() for line in text.splitlines()) + "\n" + path.write_text(normalized, encoding="utf-8") + + +def main() -> None: + OUT_DIR.mkdir(parents=True, exist_ok=True) + for index, case in enumerate(CASES): + previous_link = case_filename(CASES[index - 1]) if index > 0 else None + next_link = case_filename(CASES[index + 1]) if index < len(CASES) - 1 else None + write_html(OUT_DIR / case_filename(case), render_case(case, previous_link, next_link)) + write_html(OUT_DIR / "index.html", render_index()) + write_html(OUT_DIR / "DO_NOT_OPEN_answer_key.html", render_answer_key()) + + blind_cases = [] + answer_key = [] + for case in CASES: + blind_cases.append( + { + "id": case["id"], + "title": case["title"], + "scenario": case["scenario"], + "user_prompt": case["user_prompt"], + "rubric": case["rubric"], + "variants": { + "A": case[case["order"]["A"]], + "B": case[case["order"]["B"]], + }, + "html_report": case_filename(case), + } + ) + answer_key.append( + { + "case_id": case["id"], + "variant_a_source": case["order"]["A"], + "variant_b_source": case["order"]["B"], + "variant_a_source_label": "yao-meta-skill" if case["order"]["A"] == "yao" else "官方 skill-creator", + "variant_b_source_label": "yao-meta-skill" if case["order"]["B"] == "yao" else "官方 skill-creator", + "source_paths": { + "yao": YAO_SKILL_PATH, + "official": OFFICIAL_SKILL_PATH, + }, + } + ) + + write_json( + OUT_DIR / "blind-pack.json", + { + "schema_version": "1.0", + "seed": SEED, + "generated_by": "Codex 在本地套用两份 skill 说明生成", + "evidence_boundary": "人类盲测评审起始包;不是 provider-backed 的独立模型执行证据。", + "cases": blind_cases, + }, + ) + write_json( + OUT_DIR / "DO_NOT_OPEN_answer_key.json", + { + "schema_version": "1.0", + "seed": SEED, + "answer_key_warning": "请只在记录完所有判断后再打开。", + "answers": answer_key, + }, + ) + write_json( + OUT_DIR / "review-decisions-template.json", + { + "schema_version": "1.0", + "review_pack": "blind-human-review-2026-06-29", + "reviewer": "", + "reviewed_at": "", + "blind_review_attestation": { + "answer_key_not_opened_before_decisions": False, + "completed_before_answer_key": False, + }, + "decisions": [ + { + "case_id": case["id"], + "winner_variant": "", + "confidence": "", + "reason": "", + } + for case in CASES + ], + }, + ) + print(f"已写入盲测评审包:{OUT_DIR}") + + +if __name__ == "__main__": + main() diff --git a/reports/blind-human-review-2026-06-29/index.html b/reports/blind-human-review-2026-06-29/index.html new file mode 100644 index 0000000..00061f4 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/index.html @@ -0,0 +1,220 @@ + + + + + + 人工盲测评审包 - Meta Skill 对比 + + + +
    +

    人工盲测评审包

    +

    5 个真实常见的 skill 创建场景。打开答案钥匙前,方案来源都会保持隐藏。

    +
    +
    +
    +

    评审说明

    +

    依次打开每个案例,按评审标准比较方案 A 和方案 B,保存胜出方案和理由,然后回到这里导出判断结果。5 个判断全部保存前,不要打开 DO_NOT_OPEN_answer_key.htmlDO_NOT_OPEN_answer_key.json

    +

    证据边界:这些输出是 Codex 在本地套用两份 skill 说明生成的,适合做人类盲测评审,但不能冒充 provider-backed 的独立模型执行证据。

    +
    + + 判断模板 + 盲测包 JSON +
    + +
    +
    + +
    +
    +
    案例 1
    +

    客服工单分诊 Skill

    +

    一个 B2B SaaS 团队每天都要处理客户工单:判断分类、紧急程度、起草首轮回复,并把账务、安全、故障类问题升级给对应负责人。他们希望把这个重复流程做成一个给支持团队使用的 Codex skill。

    +
    + 打开报告 +
    + +
    +
    +
    案例 2
    +

    月度收入对账 Skill

    +

    一个财务运营团队每个月结账前都要比对 Stripe 导出、数据仓库发票 CSV 和人工调整表,找出金额、币种、发票和调整项的不一致。

    +
    + 打开报告 +
    + +
    +
    +
    案例 3
    +

    Webinar 内容复用 Skill

    +

    一个市场团队经常把 webinar 逐字稿改写成 LinkedIn 帖子、邮件 teaser 和 newsletter 段落,同时必须保证所有说法都有来源,并避开禁止宣传的 claim。

    +
    + 打开报告 +
    + +
    +
    +
    案例 4
    +

    故障复盘 Skill

    +

    一个基础设施团队经常把 Slack 故障时间线、告警数据和部署记录整理成故障复盘和后续行动项。

    +
    + 打开报告 +
    + +
    +
    +
    案例 5
    +

    PR Review 跟进 Skill

    +

    一个小工程团队经常收到 GitHub PR review comments,希望 Codex 能把评论转成补丁、回复摘要和验证记录。

    +
    + 打开报告 +
    + +
    +
    + + + diff --git a/reports/blind-human-review-2026-06-29/review-decisions-template.json b/reports/blind-human-review-2026-06-29/review-decisions-template.json new file mode 100644 index 0000000..bcb88ab --- /dev/null +++ b/reports/blind-human-review-2026-06-29/review-decisions-template.json @@ -0,0 +1,42 @@ +{ + "schema_version": "1.0", + "review_pack": "blind-human-review-2026-06-29", + "reviewer": "", + "reviewed_at": "", + "blind_review_attestation": { + "answer_key_not_opened_before_decisions": false, + "completed_before_answer_key": false + }, + "decisions": [ + { + "case_id": "case-01-support-triage", + "winner_variant": "", + "confidence": "", + "reason": "" + }, + { + "case_id": "case-02-monthly-revenue-reconciliation", + "winner_variant": "", + "confidence": "", + "reason": "" + }, + { + "case_id": "case-03-webinar-repurposing", + "winner_variant": "", + "confidence": "", + "reason": "" + }, + { + "case_id": "case-04-incident-postmortem", + "winner_variant": "", + "confidence": "", + "reason": "" + }, + { + "case_id": "case-05-pr-review-followup", + "winner_variant": "", + "confidence": "", + "reason": "" + } + ] +} diff --git a/reports/blind-human-review-2026-06-29/review-decisions.recorded.json b/reports/blind-human-review-2026-06-29/review-decisions.recorded.json new file mode 100644 index 0000000..99f7290 --- /dev/null +++ b/reports/blind-human-review-2026-06-29/review-decisions.recorded.json @@ -0,0 +1,55 @@ +{ + "schema_version": "1.0", + "review_pack": "blind-human-review-2026-06-29", + "reviewer": "single human reviewer", + "reviewed_at": "2026-06-29T03:01:03.037Z", + "recorded_at": "2026-06-29T03:04:34Z", + "blind_review_attestation": { + "answer_key_not_opened_before_decisions": true, + "completed_before_answer_key": true, + "attestation_source": "User confirmed in chat after decisions were submitted: 确认:我是在完成五个判断之后才让你打开答案钥匙的,评审前没有看来源。以上 5 个判断都是揭晓来源前完成的。" + }, + "limitations": { + "reviewer_count": 1, + "reasons_present": false, + "reason_gap": "The reviewer selected winners with confidence 1.0, but per-case rationale fields were left empty.", + "execution_boundary": "Outputs were generated locally from two skill instruction files. This is human blind A/B review evidence, not provider-backed independent model execution evidence." + }, + "decisions": [ + { + "case_id": "case-01-support-triage", + "winner_variant": "B", + "confidence": "1.0", + "reason": "", + "reviewed_at": "2026-06-29T03:00:00.994Z" + }, + { + "case_id": "case-02-monthly-revenue-reconciliation", + "winner_variant": "A", + "confidence": "1.0", + "reason": "", + "reviewed_at": "2026-06-29T03:00:18.277Z" + }, + { + "case_id": "case-03-webinar-repurposing", + "winner_variant": "B", + "confidence": "1.0", + "reason": "", + "reviewed_at": "2026-06-29T03:00:34.343Z" + }, + { + "case_id": "case-04-incident-postmortem", + "winner_variant": "A", + "confidence": "1.0", + "reason": "", + "reviewed_at": "2026-06-29T03:00:49.160Z" + }, + { + "case_id": "case-05-pr-review-followup", + "winner_variant": "B", + "confidence": "1.0", + "reason": "", + "reviewed_at": "2026-06-29T03:01:03.037Z" + } + ] +} diff --git a/reports/candidate_registry.json b/reports/candidate_registry.json new file mode 100644 index 0000000..3880518 --- /dev/null +++ b/reports/candidate_registry.json @@ -0,0 +1,789 @@ +{ + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "winner_tokens": 54, + "current_tokens": 54, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "blind_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "adversarial_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.644, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "yao-meta-skill", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Create and improve agent skills.", + "tokens": 8, + "dev_errors": 1, + "holdout_errors": 0 + }, + { + "name": "yao-meta-skill", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Minimal", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 41, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 56, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 60, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Trigger when requests mention workflows, prompts, transcripts, or docs and the job is to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 77, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 83, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.089, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.496, + "max_non_trigger_score": 0.211, + "score_gap": 0.285, + "threshold_margin": 0.016, + "positive_threshold_buffer": 0.016, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.805, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "team-frontend-review", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "tokens": 52, + "dev_errors": 3, + "holdout_errors": 0 + }, + { + "name": "team-frontend-review", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 62, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 64, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Trigger when requests mention React components, forms, frontend diffs, or UI code and the job is to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 84, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 90, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 1, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.45, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.037, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.057, + "score_gap": 0.023, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.423, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.82, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.645, + "max_non_trigger_score": 0.228, + "score_gap": 0.417, + "threshold_margin": 0.165, + "positive_threshold_buffer": 0.165, + "negative_threshold_buffer": 0.252, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.907, + "mean_non_trigger_score": 0.083, + "mean_near_neighbor_score": 0.139, + "min_positive_score": 0.815, + "max_non_trigger_score": 0.277, + "score_gap": 0.538, + "threshold_margin": 0.063, + "positive_threshold_buffer": 0.475, + "negative_threshold_buffer": 0.063, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_gap_present", + "current_holdout_risk", + "adversarial_watch_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.709, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "governed-incident-command", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, and stakeholder requirements. Use when asked to standardize incident review, severity assessment, incident communication, or incident packet assembly into one reusable operational skill. Do not use for generic debugging, one-off incident explanations, or brainstorming possible response ideas.", + "tokens": 93, + "dev_errors": 0, + "holdout_errors": 1 + }, + { + "name": "governed-incident-command", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Trigger when requests mention alerts, incident notes, transcripts, or stakeholder requirements and the job is to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 51, + "dev_errors": 1, + "holdout_errors": 1, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + } + ] +} diff --git a/reports/candidate_registry.md b/reports/candidate_registry.md new file mode 100644 index 0000000..fe35c95 --- /dev/null +++ b/reports/candidate_registry.md @@ -0,0 +1,23 @@ +# Candidate Registry + +| Target | Role | Label | Ranking State | Promotion State | Tokens | Dev Errors | Holdout Errors | Reason Tags | +| --- | --- | --- | --- | --- | ---: | ---: | ---: | --- | +| `yao-meta-skill` | baseline | `Baseline` | reference | reference | 8 | 1 | 0 | - | +| `yao-meta-skill` | current | `Current` | selected_by_dev | kept_current | 54 | 0 | 0 | - | +| `yao-meta-skill` | candidate | `Minimal` | not_selected | blocked | 41 | 2 | 0 | weaker_dev_fit | +| `yao-meta-skill` | candidate | `Guardrail` | not_selected | blocked | 56 | 2 | 0 | weaker_dev_fit, longer_without_gain | +| `yao-meta-skill` | candidate | `Balanced` | not_selected | blocked | 60 | 2 | 0 | weaker_dev_fit, longer_without_gain | +| `yao-meta-skill` | candidate | `Artifact Aware` | not_selected | blocked | 77 | 2 | 0 | weaker_dev_fit, longer_without_gain | +| `yao-meta-skill` | candidate | `Boundary` | not_selected | blocked | 83 | 2 | 0 | weaker_dev_fit, longer_without_gain | +| `team-frontend-review` | baseline | `Baseline` | reference | reference | 52 | 3 | 0 | - | +| `team-frontend-review` | current | `Current` | selected_by_dev | kept_current | 50 | 3 | 0 | - | +| `team-frontend-review` | candidate | `Guardrail` | not_selected | blocked | 62 | 3 | 0 | longer_without_gain | +| `team-frontend-review` | candidate | `Balanced` | not_selected | blocked | 64 | 3 | 0 | longer_without_gain | +| `team-frontend-review` | candidate | `Artifact Aware` | not_selected | blocked | 84 | 3 | 0 | longer_without_gain | +| `team-frontend-review` | candidate | `Boundary` | not_selected | blocked | 90 | 3 | 0 | longer_without_gain | +| `governed-incident-command` | baseline | `Baseline` | reference | reference | 93 | 0 | 1 | - | +| `governed-incident-command` | current | `Current` | selected_by_dev | kept_current | 37 | 0 | 1 | - | +| `governed-incident-command` | candidate | `Balanced` | not_selected | blocked | 54 | 0 | 1 | longer_without_gain | +| `governed-incident-command` | candidate | `Boundary` | not_selected | blocked | 78 | 0 | 1 | longer_without_gain | +| `governed-incident-command` | candidate | `Artifact Aware` | not_selected | blocked | 78 | 0 | 1 | longer_without_gain | +| `governed-incident-command` | candidate | `Guardrail` | not_selected | blocked | 51 | 1 | 1 | weaker_dev_fit, longer_without_gain | diff --git a/reports/compiled_targets.json b/reports/compiled_targets.json new file mode 100644 index 0000000..98d3f8c --- /dev/null +++ b/reports/compiled_targets.json @@ -0,0 +1,4769 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-13", + "skill_dir": ".", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "schema_version": "1.0", + "target": "openai", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "openai", + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "degradation_strategy": "metadata-adapter", + "target_runtime": { + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "target_transform": { + "target": "openai", + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "metadata-adapter", + "permission_representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation_policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "resource_strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "script_strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "permission_enforcement": "metadata-only" + }, + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "claude", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "claude", + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "degradation_strategy": "neutral-source-plus-adapter", + "target_runtime": { + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Claude-compatible neutral source folder with adapter notes" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "target_transform": { + "target": "claude", + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source-plus-adapter", + "permission_representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation_policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "resource_strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "script_strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "permission_enforcement": "metadata-fallback" + }, + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "generic", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "generic", + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills compatible neutral package" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "target_transform": { + "target": "generic", + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "targets/generic/adapter.json::target_permission_contract", + "native_surface": "Agent Skills compatible neutral package", + "activation_policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "resource_strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "script_strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "agent-skills-compatible", + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills standard source tree" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "target_transform": { + "target": "agent-skills-compatible", + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "adapter metadata", + "native_surface": "Agent Skills standard source tree", + "activation_policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "resource_strategy": "Keep optional directories as relative resources next to SKILL.md.", + "script_strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "vscode", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "vscode", + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "target_runtime": { + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "VS Code/Copilot Agent Skills project or user scope" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "target_transform": { + "target": "vscode", + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "permission_representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation_policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "resource_strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "script_strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "permission_enforcement": "client-or-workspace-trust" + }, + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [], + "failures": [] + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "json": "reports/compiled_targets.json", + "markdown": "reports/compiled_targets.md" + } +} diff --git a/reports/compiled_targets.md b/reports/compiled_targets.md new file mode 100644 index 0000000..f6a11f9 --- /dev/null +++ b/reports/compiled_targets.md @@ -0,0 +1,73 @@ +# Compiled Targets + +- OK: `True` +- Targets: `5` +- Pass: `5` +- Warn: `0` +- Block: `0` + +## Target Transforms + +| Target | Status | Native Surface | Adapter Mode | Permissions | Degradation | Generated Files | +| --- | --- | --- | --- | --- | --- | --- | +| `openai` | `pass` | OpenAI-style interface metadata plus neutral Agent Skills source | `metadata-adapter` | `network, file_write, subprocess` | `metadata-adapter` | targets/openai/adapter.json, targets/openai/agents/openai.yaml | +| `claude` | `pass` | Claude-compatible neutral source folder with adapter notes | `neutral-source-plus-adapter` | `network, file_write, subprocess` | `neutral-source-plus-adapter` | targets/claude/adapter.json, targets/claude/README.md | +| `generic` | `pass` | Agent Skills compatible neutral package | `agent-skills-compatible` | `network, file_write, subprocess` | `neutral-source` | targets/generic/adapter.json | +| `agent-skills-compatible` | `pass` | Agent Skills standard source tree | `neutral-agent-skills-source` | `network, file_write, subprocess` | `neutral-source` | SKILL.md, agents/interface.yaml | +| `vscode` | `pass` | VS Code/Copilot Agent Skills project or user scope | `vscode-agent-skills-adapter` | `network, file_write, subprocess` | `agent-skills-source-with-vscode-notes` | targets/vscode/adapter.json, targets/vscode/README.md | + +## Native Behavior Contracts + +### openai + +- Native surface: OpenAI-style interface metadata plus neutral Agent Skills source +- Activation: Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata. +- Resources: Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file. +- Scripts: Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution. +- Permission enforcement: `metadata-only`; native enforcement `False` +- Review artifacts: targets/openai/agents/openai.yaml, targets/openai/adapter.json, reports/review-studio.html + +### claude + +- Native surface: Claude-compatible neutral source folder with adapter notes +- Activation: Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata. +- Resources: Preserve the source tree directly; write target notes in targets/claude/README.md. +- Scripts: Scripts remain local package resources and must be reviewed through trust and permission reports before use. +- Permission enforcement: `metadata-fallback`; native enforcement `False` +- Review artifacts: targets/claude/README.md, targets/claude/adapter.json, reports/review-studio.html + +### generic + +- Native surface: Agent Skills compatible neutral package +- Activation: Use SKILL.md name and description; consumers decide automatic or manual activation. +- Resources: Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources. +- Scripts: Expose script and permission metadata for downstream clients or installers to enforce. +- Permission enforcement: `consumer-enforced-or-metadata-only`; native enforcement `False` +- Review artifacts: targets/generic/adapter.json, reports/review-studio.html + +### agent-skills-compatible + +- Native surface: Agent Skills standard source tree +- Activation: Use SKILL.md frontmatter name and description for progressive disclosure. +- Resources: Keep optional directories as relative resources next to SKILL.md. +- Scripts: Scripts remain local optional resources and should advertise --help when executable. +- Permission enforcement: `consumer-enforced-or-metadata-only`; native enforcement `False` +- Review artifacts: SKILL.md, agents/interface.yaml, reports/review-studio.html + +### vscode + +- Native surface: VS Code/Copilot Agent Skills project or user scope +- Activation: Use folder name plus SKILL.md name/description; keep description under platform limits. +- Resources: Install as project or user scoped skill source, preserving relative references and scripts. +- Scripts: Scripts require workspace trust and operator/client approval outside this compiler. +- Permission enforcement: `client-or-workspace-trust`; native enforcement `False` +- Review artifacts: SKILL.md, agents/interface.yaml, reports/review-studio.html + + +## Failures + +- None + +## Warnings + +- None diff --git a/reports/conformance_matrix.json b/reports/conformance_matrix.json new file mode 100644 index 0000000..722a8ef --- /dev/null +++ b/reports/conformance_matrix.json @@ -0,0 +1,239 @@ +{ + "ok": true, + "skill": "yao-meta-skill", + "targets": [ + { + "target": "openai", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares openai", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "adapter target declares openai", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "openai degradation note exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares claude", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "adapter target declares claude", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "claude degradation note exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [] + }, + { + "target": "agent-skills", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "package identity derives from skill name", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares agent-skills via agent-skills-compatible", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "canonical format is agent-skills", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [ + "agent-skills uses canonical Agent Skills metadata; provider-native execution transforms are not implemented in v0." + ] + }, + { + "target": "vscode", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "package identity derives from skill name", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares vscode", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "canonical format is agent-skills", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [ + "vscode uses canonical Agent Skills metadata; provider-native execution transforms are not implemented in v0." + ] + }, + { + "target": "generic", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares generic", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "adapter target declares generic", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "generic degradation note exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [] + } + ], + "summary": { + "target_count": 5, + "pass_count": 5, + "fail_count": 0 + }, + "artifacts": { + "json": "reports/conformance_matrix.json", + "markdown": "reports/conformance_matrix.md" + } +} diff --git a/reports/conformance_matrix.md b/reports/conformance_matrix.md new file mode 100644 index 0000000..243e9bd --- /dev/null +++ b/reports/conformance_matrix.md @@ -0,0 +1,19 @@ +# Runtime Conformance Matrix + +- Skill: `yao-meta-skill` +- Targets: `5` +- Passed: `5` +- Failed: `0` + +| Target | Status | Failures | Warnings | +| --- | --- | --- | --- | +| openai | pass | None | None | +| claude | pass | None | None | +| agent-skills | pass | None | agent-skills uses canonical Agent Skills metadata; provider-native execution transforms are not implemented in v0. | +| vscode | pass | None | vscode uses canonical Agent Skills metadata; provider-native execution transforms are not implemented in v0. | +| generic | pass | None | None | + +## Reviewer Notes + +- Failed targets block release for that target. +- Warnings identify lossy or not-yet-compiled behavior that must remain visible. diff --git a/reports/context_budget.json b/reports/context_budget.json new file mode 100644 index 0000000..28065fb --- /dev/null +++ b/reports/context_budget.json @@ -0,0 +1,74 @@ +{ + "ok": true, + "failures": [], + "warnings": [], + "stats": { + "context_budget_tier": "production", + "context_budget_limit": 1000, + "skill_body_tokens": 794, + "other_text_tokens": 1200569, + "estimated_initial_load_tokens": 987, + "estimated_total_text_tokens": 1201363, + "deferred_resource_tokens": 531767, + "deferred_resource_warn_threshold": 120000, + "deferred_resource_dirs": [ + { + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155 + }, + { + "path": "references", + "estimated_tokens": 28292, + "file_count": 35 + }, + { + "path": "evals", + "estimated_tokens": 22731, + "file_count": 27 + }, + { + "path": "assets", + "estimated_tokens": 11176, + "file_count": 4 + } + ], + "large_deferred_resource_dirs": [ + { + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155 + } + ], + "deferred_resource_governance": { + "status": "governed", + "large_dir_count": 1, + "governed_large_dir_count": 1, + "directories": [ + { + "status": "governed", + "evidence": [ + "reports/security_trust_report.json", + "reports/architecture_maintainability.json", + "reports/python_compatibility.json" + ], + "reasons": [ + "trust report covers scripts", + "architecture report has no script hotspots or blockers", + "Python compatibility report has no issues" + ], + "missing": [], + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155, + "rationale": "Script resources are deterministic deferred tools, not initial-load prompt context." + } + ], + "summary": "Large deferred resources are indexed and backed by evidence." + }, + "relevant_file_count": 591, + "unused_resource_dirs": [], + "quality_signal_points": 140, + "quality_density": 141.8 + } +} diff --git a/reports/context_budget.md b/reports/context_budget.md new file mode 100644 index 0000000..1127edf --- /dev/null +++ b/reports/context_budget.md @@ -0,0 +1,9 @@ +# Context Budget Summary + +| Target | Path | Tier | Limit | Initial | SKILL | Deferred | Resource Governance | Large Deferred Dirs | Quality Density | Unused Dirs | Status | +| --- | --- | --- | ---: | ---: | ---: | ---: | --- | --- | ---: | --- | --- | +| root | `.` | `production` | 1000 | 987 | 794 | 531767 | `governed` | scripts:469568 | 141.8 | - | ok | +| complex-release-orchestrator | `examples/complex-release-orchestrator/generated-skill` | `production` | 1000 | 790 | 718 | 1657 | `not-required` | - | 164.6 | - | ok | +| governed-incident-command | `examples/governed-incident-command/generated-skill` | `production` | 1000 | 760 | 658 | 1030 | `not-required` | - | 171.1 | - | ok | + +Per-target JSON reports are written beside each target and under `reports/`. diff --git a/reports/context_budget_summary.json b/reports/context_budget_summary.json new file mode 100644 index 0000000..c198f8d --- /dev/null +++ b/reports/context_budget_summary.json @@ -0,0 +1,93 @@ +{ + "generated_at": "2026-03-31", + "targets": [ + { + "label": "root", + "path": ".", + "budget_tier": "production", + "budget_limit": 1000, + "initial_tokens": 987, + "skill_body_tokens": 794, + "deferred_resource_tokens": 531767, + "large_deferred_resource_dirs": [ + { + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155 + } + ], + "deferred_resource_governance": { + "status": "governed", + "large_dir_count": 1, + "governed_large_dir_count": 1, + "directories": [ + { + "status": "governed", + "evidence": [ + "reports/security_trust_report.json", + "reports/architecture_maintainability.json", + "reports/python_compatibility.json" + ], + "reasons": [ + "trust report covers scripts", + "architecture report has no script hotspots or blockers", + "Python compatibility report has no issues" + ], + "missing": [], + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155, + "rationale": "Script resources are deterministic deferred tools, not initial-load prompt context." + } + ], + "summary": "Large deferred resources are indexed and backed by evidence." + }, + "quality_density": 141.8, + "unused_resource_dirs": [], + "ok": true, + "warnings": [] + }, + { + "label": "complex-release-orchestrator", + "path": "examples/complex-release-orchestrator/generated-skill", + "budget_tier": "production", + "budget_limit": 1000, + "initial_tokens": 790, + "skill_body_tokens": 718, + "deferred_resource_tokens": 1657, + "large_deferred_resource_dirs": [], + "deferred_resource_governance": { + "status": "not-required", + "large_dir_count": 0, + "governed_large_dir_count": 0, + "directories": [], + "summary": "No large deferred resource directory exceeds the per-dir threshold." + }, + "quality_density": 164.6, + "unused_resource_dirs": [], + "ok": true, + "warnings": [] + }, + { + "label": "governed-incident-command", + "path": "examples/governed-incident-command/generated-skill", + "budget_tier": "production", + "budget_limit": 1000, + "initial_tokens": 760, + "skill_body_tokens": 658, + "deferred_resource_tokens": 1030, + "large_deferred_resource_dirs": [], + "deferred_resource_governance": { + "status": "not-required", + "large_dir_count": 0, + "governed_large_dir_count": 0, + "directories": [], + "summary": "No large deferred resource directory exceeds the per-dir threshold." + }, + "quality_density": 171.1, + "unused_resource_dirs": [], + "ok": true, + "warnings": [] + } + ] +} diff --git a/reports/description_drift_history.md b/reports/description_drift_history.md new file mode 100644 index 0000000..33a037d --- /dev/null +++ b/reports/description_drift_history.md @@ -0,0 +1,33 @@ +# Description Drift History + +| Date | Label | Target | Winner | Tokens | Holdout FP | Holdout FN | Blind FP | Blind FN | Judge Blind Errors | Judge Agreement | Adv FP | Adv FN | Adv Gap | Adv Risk | Drift Note | +| --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- | +| 2026-03-31 | Description Optimization Suite | `yao-meta-skill` | `Current` | 65 | 0 | 0 | - | - | - | - | - | - | - | - | initial description optimization suite without blind holdout | +| 2026-03-31 | Description Optimization Suite | `team-frontend-review` | `Current` | 50 | 0 | 0 | - | - | - | - | - | - | - | - | compressed example description and stored first holdout report | +| 2026-03-31 | Description Optimization Suite | `governed-incident-command` | `Current` | 37 | 0 | 1 | - | - | - | - | - | - | - | - | compressed governed description and reduced visible holdout misses | +| 2026-04-01 | Adversarial Calibration And Family Drift | `yao-meta-skill` | `Current` | 65 | 0 | 0 | 0 | 0 | - | - | 0 | 0 | 0.834 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.834; risk n/a -> healthy | +| 2026-04-01 | Adversarial Calibration And Family Drift | `team-frontend-review` | `Current` | 50 | 0 | 0 | 0 | 0 | - | - | 0 | 0 | 0.509 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.509; risk n/a -> healthy | +| 2026-04-01 | Adversarial Calibration And Family Drift | `governed-incident-command` | `Current` | 37 | 0 | 1 | 0 | 0 | - | - | 0 | 0 | 0.598 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 1; adversarial calibration +0.598; risk n/a -> healthy | +| 2026-04-01 | Blind Holdout And Drift History | `yao-meta-skill` | `Current` | 65 | 0 | 0 | 0 | 0 | - | - | - | - | - | - | tokens stable; blind gate added with 0 errors; holdout stable at 0 | +| 2026-04-01 | Blind Holdout And Drift History | `team-frontend-review` | `Current` | 50 | 0 | 0 | 0 | 0 | - | - | - | - | - | - | tokens stable; blind gate added with 0 errors; holdout stable at 0 | +| 2026-04-01 | Blind Holdout And Drift History | `governed-incident-command` | `Current` | 37 | 0 | 1 | 0 | 0 | - | - | - | - | - | - | tokens stable; blind gate added with 0 errors; holdout stable at 1 | +| 2026-04-01 | Judge-Backed Blind Eval | `yao-meta-skill` | `Current` | 65 | 0 | 0 | 0 | 0 | 0 | 1.0 | 0 | 0 | 0.834 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.834; risk n/a -> healthy | +| 2026-04-01 | Judge-Backed Blind Eval | `team-frontend-review` | `Current` | 50 | 0 | 0 | 0 | 0 | 0 | 1.0 | 0 | 0 | 0.509 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.509; risk n/a -> healthy | +| 2026-04-01 | Judge-Backed Blind Eval | `governed-incident-command` | `Current` | 37 | 0 | 1 | 0 | 0 | 0 | 1.0 | 0 | 0 | 0.598 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 1; adversarial calibration +0.598; risk n/a -> healthy | + +## Family Coverage + +| Date | Label | Target | Blind Families | Judge Blind Families | Adversarial Families | +| --- | --- | --- | --- | --- | --- | +| 2026-03-31 | Description Optimization Suite | `yao-meta-skill` | - | - | - | +| 2026-03-31 | Description Optimization Suite | `team-frontend-review` | - | - | - | +| 2026-03-31 | Description Optimization Suite | `governed-incident-command` | - | - | - | +| 2026-04-01 | Adversarial Calibration And Family Drift | `yao-meta-skill` | 6/6 clean; weakest=blind_operationalize | - | 6/6 clean; weakest=adversarial_summary_translation_collision | +| 2026-04-01 | Adversarial Calibration And Family Drift | `team-frontend-review` | 6/6 clean; weakest=blind_premerge | - | 6/6 clean; weakest=adversarial_quality_gate_review | +| 2026-04-01 | Adversarial Calibration And Family Drift | `governed-incident-command` | 6/6 clean; weakest=blind_summary_only | - | 6/6 clean; weakest=adversarial_single_update_collision | +| 2026-04-01 | Blind Holdout And Drift History | `yao-meta-skill` | - | - | - | +| 2026-04-01 | Blind Holdout And Drift History | `team-frontend-review` | - | - | - | +| 2026-04-01 | Blind Holdout And Drift History | `governed-incident-command` | - | - | - | +| 2026-04-01 | Judge-Backed Blind Eval | `yao-meta-skill` | 6/6 clean; weakest=blind_operationalize | 6/6 clean; weakest=blind_operationalize | 6/6 clean; weakest=adversarial_summary_translation_collision | +| 2026-04-01 | Judge-Backed Blind Eval | `team-frontend-review` | 6/6 clean; weakest=blind_premerge | 6/6 clean; weakest=blind_premerge | 6/6 clean; weakest=adversarial_quality_gate_review | +| 2026-04-01 | Judge-Backed Blind Eval | `governed-incident-command` | 6/6 clean; weakest=blind_summary_only | 6/6 clean; weakest=blind_summary_only | 6/6 clean; weakest=adversarial_single_update_collision | diff --git a/reports/description_optimization.json b/reports/description_optimization.json new file mode 100644 index 0000000..410954d --- /dev/null +++ b/reports/description_optimization.json @@ -0,0 +1,10520 @@ +{ + "current_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "current_candidate": { + "id": "current", + "label": "Current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "strategy": "current", + "estimated_tokens": 54, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 54 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "baseline": { + "description": "Create and improve agent skills.", + "estimated_tokens": 8, + "dev": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.875, + "bucket_stats": { + "should_trigger": { + "total": 8, + "passed": 7, + "pass_rate": 0.875 + }, + "should_not_trigger": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 1, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.5 + }, + "complex_multi_asset": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "iterate_existing_skill", + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "score": 0.328, + "reason": "false_negative", + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill", + "score": 0.387, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist", + "operations checklist" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill", + "score": 0.328, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.302, + "support_score": 0.04, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "trigger evals" + ], + "iterate_existing_skill": [ + "this skill", + "sharing it with the team", + "before sharing it with the team" + ], + "team_operationalize": [ + "team", + "sharing it with the team" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset", + "score": 0.844, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.042, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package", + "turn all of that into one reusable skill package" + ], + "eval_optimize": [ + "evals", + "packaging checks" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "runbook", + "process", + "prompt history" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset", + "score": 0.408, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0.06, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "production-ready" + ], + "transform_workflow": [ + "workflow", + "workflow fragments", + "scripts" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill", + "score": 0.527, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.491, + "support_score": 0.04, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "distribution" + ], + "team_operationalize": [ + "team", + "distribution" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger", + "score": 0.403, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0.04, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "team library", + "maintained" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger", + "score": 0.856, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0.04, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this workflow" + ], + "eval_optimize": [ + "validate", + "route boundary" + ], + "reuse_package": [ + "package", + "distribution", + "shared capability" + ], + "team_operationalize": [ + "shared", + "distribution" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger", + "score": 0.869, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset", + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0.1, + "lexical_support": 0.038, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill", + "skill package" + ], + "eval_optimize": [ + "validation" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "rough notes", + "transcript", + "onboarding flow", + "scripts" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Give me ideas for improving our process.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "ideas" + ] + } + } + } + }, + { + "prompt": "Review this note and tell me what it means in plain English.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "tell me what it means", + "plain english" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0.06, + "lexical_support": 0.067, + "negative_penalty": 0.66, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "improve packaging" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "translate_only": [ + "translate", + "into french" + ] + } + } + } + }, + { + "prompt": "Summarize this workflow and list the main points only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.111, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.043, + "negative_penalty": 0.68, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "explain_only": [ + "explain it", + "in simpler language" + ], + "no_build_directive": [ + "do not convert it into a reusable capability", + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only", + "score": 0.257, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.698, + "support_score": 0, + "lexical_support": 0.056, + "negative_penalty": 0.3, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "summarize_only": [ + "summary", + "recap" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "explain_only": [ + "explain it" + ], + "no_build_directive": [ + "no packaging needed" + ] + } + } + } + }, + { + "prompt": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.42, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + }, + { + "prompt": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document" + ], + "no_build_directive": [ + "not as an agent skill" + ] + } + } + } + }, + { + "prompt": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.34, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "document_only": [ + "document", + "reusable-looking document" + ], + "no_build_directive": [ + "without turning them into an agent skill" + ] + } + } + } + }, + { + "prompt": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.048, + "negative_penalty": 0.82, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document", + "template only" + ], + "future_outline": [ + "not now", + "later but not now" + ], + "no_build_directive": [ + "not now", + "later but not now" + ] + } + } + } + }, + { + "prompt": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.053, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "for later", + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + } + ] + } + }, + "holdout": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 5, + "passed": 5, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + } + }, + "family_stats": { + "complex_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset", + "score": 0.416, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset", + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0.1, + "lexical_support": 0.053, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable" + ], + "team_operationalize": [ + "internal" + ], + "transform_workflow": [ + "process", + "internal process note", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill", + "score": 0.699, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.679, + "support_score": 0.04, + "lexical_support": 0.056, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "near-neighbor", + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "package", + "distribution" + ], + "team_operationalize": [ + "distribution" + ], + "transform_workflow": [ + "existing skill draft" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill", + "score": 0.841, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill library entry" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "library entry" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger", + "score": 0.861, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0.06, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "packaging checks" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "quarterly release routine", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger", + "score": 0.719, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.698, + "support_score": 0.04, + "lexical_support": 0.08, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "turn this playbook into an agent capability package", + "agent capability" + ], + "reuse_package": [ + "package", + "team library", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "prompt history", + "playbook", + "transcript", + "release notes" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ], + "summarize_only": [ + "summary", + "only want a summary" + ] + } + } + } + }, + { + "prompt": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package", + "release" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "explain_only": [ + "explain this", + "in simpler language" + ], + "no_build_directive": [ + "do not package it" + ] + } + } + } + }, + { + "prompt": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.111, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "release" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document" + ], + "no_build_directive": [ + "not for agent execution" + ] + } + } + } + }, + { + "prompt": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.698, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into russian" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "semantic_coverage": 0.321, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.46, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ] + }, + "negative": { + "brainstorm_only": [ + "shape an idea" + ], + "future_outline": [ + "before we decide whether to build" + ] + } + } + } + }, + { + "prompt": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill", + "outline for a possible future skill" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document", + "document package", + "wiki" + ], + "no_build_directive": [ + "not an agent skill" + ] + } + } + } + }, + { + "prompt": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.698, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "workflow", + "checklist" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "no skill package", + "discussion only" + ] + } + } + } + } + ] + } + } + }, + "winner": { + "id": "current", + "label": "Current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "strategy": "current", + "estimated_tokens": 54, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 54 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "winner_dev_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 8, + "passed": 8, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "complex_multi_asset": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill", + "score": 0.379, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist", + "operations checklist" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill", + "score": 0.333, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.286, + "support_score": 0.04, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "trigger evals" + ], + "iterate_existing_skill": [ + "this skill", + "sharing it with the team", + "before sharing it with the team" + ], + "team_operationalize": [ + "team", + "sharing it with the team" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset", + "score": 0.816, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.208, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package", + "turn all of that into one reusable skill package" + ], + "eval_optimize": [ + "evals", + "packaging checks" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "runbook", + "process", + "prompt history" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset", + "score": 0.454, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "production-ready" + ], + "transform_workflow": [ + "workflow", + "workflow fragments", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill", + "score": 0.514, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.464, + "support_score": 0.04, + "lexical_support": 0.214, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "distribution" + ], + "team_operationalize": [ + "team", + "distribution" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger", + "score": 0.39, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0.04, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "team library", + "maintained" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0.04, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this workflow" + ], + "eval_optimize": [ + "validate", + "route boundary" + ], + "reuse_package": [ + "package", + "distribution", + "shared capability" + ], + "team_operationalize": [ + "shared", + "distribution" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger", + "score": 0.871, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0.04, + "lexical_support": 0.154, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill", + "skill package" + ], + "eval_optimize": [ + "validation" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "rough notes", + "transcript", + "onboarding flow", + "scripts" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Give me ideas for improving our process.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "ideas" + ] + } + } + } + }, + { + "prompt": "Review this note and tell me what it means in plain English.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "tell me what it means", + "plain english" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.133, + "negative_penalty": 0.66, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "improve packaging" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "translate_only": [ + "translate", + "into french" + ] + } + } + } + }, + { + "prompt": "Summarize this workflow and list the main points only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.68, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "explain_only": [ + "explain it", + "in simpler language" + ], + "no_build_directive": [ + "do not convert it into a reusable capability", + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only", + "score": 0.236, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0.3, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "summarize_only": [ + "summary", + "recap" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "explain_only": [ + "explain it" + ], + "no_build_directive": [ + "no packaging needed" + ] + } + } + } + }, + { + "prompt": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.42, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + }, + { + "prompt": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document" + ], + "no_build_directive": [ + "not as an agent skill" + ] + } + } + } + }, + { + "prompt": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.321, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "document_only": [ + "document", + "reusable-looking document" + ], + "no_build_directive": [ + "without turning them into an agent skill" + ] + } + } + } + }, + { + "prompt": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.048, + "negative_penalty": 0.82, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document", + "template only" + ], + "future_outline": [ + "not now", + "later but not now" + ], + "no_build_directive": [ + "not now", + "later but not now" + ] + } + } + } + }, + { + "prompt": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "for later", + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + } + ] + } + }, + "winner_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 5, + "passed": 5, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + } + }, + "family_stats": { + "complex_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset", + "score": 0.465, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0.04, + "lexical_support": 0.211, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable" + ], + "team_operationalize": [ + "internal" + ], + "transform_workflow": [ + "process", + "internal process note", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill", + "score": 0.684, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.643, + "support_score": 0.04, + "lexical_support": 0.278, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "near-neighbor", + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "package", + "distribution" + ], + "team_operationalize": [ + "distribution" + ], + "transform_workflow": [ + "existing skill draft" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill", + "score": 0.805, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill library entry" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "library entry" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger", + "score": 0.874, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "packaging checks" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "quarterly release routine", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger", + "score": 0.691, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.661, + "support_score": 0.04, + "lexical_support": 0.16, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "turn this playbook into an agent capability package", + "agent capability" + ], + "reuse_package": [ + "package", + "team library", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "prompt history", + "playbook", + "transcript", + "release notes" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ], + "summarize_only": [ + "summary", + "only want a summary" + ] + } + } + } + }, + { + "prompt": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package", + "release" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "explain_only": [ + "explain this", + "in simpler language" + ], + "no_build_directive": [ + "do not package it" + ] + } + } + } + }, + { + "prompt": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.167, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "release" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document" + ], + "no_build_directive": [ + "not for agent execution" + ] + } + } + } + }, + { + "prompt": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.188, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into russian" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "semantic_coverage": 0.304, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.46, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ] + }, + "negative": { + "brainstorm_only": [ + "shape an idea" + ], + "future_outline": [ + "before we decide whether to build" + ] + } + } + } + }, + { + "prompt": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.214, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill", + "outline for a possible future skill" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.308, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document", + "document package", + "wiki" + ], + "no_build_directive": [ + "not an agent skill" + ] + } + } + } + }, + { + "prompt": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.375, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "workflow", + "checklist" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "no skill package", + "discussion only" + ] + } + } + } + } + ] + } + }, + "current_dev_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 8, + "passed": 8, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "complex_multi_asset": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill", + "score": 0.379, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist", + "operations checklist" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill", + "score": 0.333, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.286, + "support_score": 0.04, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "trigger evals" + ], + "iterate_existing_skill": [ + "this skill", + "sharing it with the team", + "before sharing it with the team" + ], + "team_operationalize": [ + "team", + "sharing it with the team" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset", + "score": 0.816, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.208, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package", + "turn all of that into one reusable skill package" + ], + "eval_optimize": [ + "evals", + "packaging checks" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "runbook", + "process", + "prompt history" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset", + "score": 0.454, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "production-ready" + ], + "transform_workflow": [ + "workflow", + "workflow fragments", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill", + "score": 0.514, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.464, + "support_score": 0.04, + "lexical_support": 0.214, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "distribution" + ], + "team_operationalize": [ + "team", + "distribution" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger", + "score": 0.39, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0.04, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "team library", + "maintained" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0.04, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this workflow" + ], + "eval_optimize": [ + "validate", + "route boundary" + ], + "reuse_package": [ + "package", + "distribution", + "shared capability" + ], + "team_operationalize": [ + "shared", + "distribution" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger", + "score": 0.871, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0.04, + "lexical_support": 0.154, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill", + "skill package" + ], + "eval_optimize": [ + "validation" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "rough notes", + "transcript", + "onboarding flow", + "scripts" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Give me ideas for improving our process.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "ideas" + ] + } + } + } + }, + { + "prompt": "Review this note and tell me what it means in plain English.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "tell me what it means", + "plain english" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.133, + "negative_penalty": 0.66, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "improve packaging" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "translate_only": [ + "translate", + "into french" + ] + } + } + } + }, + { + "prompt": "Summarize this workflow and list the main points only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.68, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "explain_only": [ + "explain it", + "in simpler language" + ], + "no_build_directive": [ + "do not convert it into a reusable capability", + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only", + "score": 0.236, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0.3, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "summarize_only": [ + "summary", + "recap" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "explain_only": [ + "explain it" + ], + "no_build_directive": [ + "no packaging needed" + ] + } + } + } + }, + { + "prompt": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.42, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + }, + { + "prompt": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document" + ], + "no_build_directive": [ + "not as an agent skill" + ] + } + } + } + }, + { + "prompt": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.321, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "document_only": [ + "document", + "reusable-looking document" + ], + "no_build_directive": [ + "without turning them into an agent skill" + ] + } + } + } + }, + { + "prompt": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.048, + "negative_penalty": 0.82, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document", + "template only" + ], + "future_outline": [ + "not now", + "later but not now" + ], + "no_build_directive": [ + "not now", + "later but not now" + ] + } + } + } + }, + { + "prompt": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "for later", + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + } + ] + } + }, + "current_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 5, + "passed": 5, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + } + }, + "family_stats": { + "complex_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset", + "score": 0.465, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0.04, + "lexical_support": 0.211, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable" + ], + "team_operationalize": [ + "internal" + ], + "transform_workflow": [ + "process", + "internal process note", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill", + "score": 0.684, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.643, + "support_score": 0.04, + "lexical_support": 0.278, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "near-neighbor", + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "package", + "distribution" + ], + "team_operationalize": [ + "distribution" + ], + "transform_workflow": [ + "existing skill draft" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill", + "score": 0.805, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill library entry" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "library entry" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger", + "score": 0.874, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "packaging checks" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "quarterly release routine", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger", + "score": 0.691, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.661, + "support_score": 0.04, + "lexical_support": 0.16, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "turn this playbook into an agent capability package", + "agent capability" + ], + "reuse_package": [ + "package", + "team library", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "prompt history", + "playbook", + "transcript", + "release notes" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ], + "summarize_only": [ + "summary", + "only want a summary" + ] + } + } + } + }, + { + "prompt": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package", + "release" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "explain_only": [ + "explain this", + "in simpler language" + ], + "no_build_directive": [ + "do not package it" + ] + } + } + } + }, + { + "prompt": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.167, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "release" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document" + ], + "no_build_directive": [ + "not for agent execution" + ] + } + } + } + }, + { + "prompt": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.188, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into russian" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "semantic_coverage": 0.304, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.46, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ] + }, + "negative": { + "brainstorm_only": [ + "shape an idea" + ], + "future_outline": [ + "before we decide whether to build" + ] + } + } + } + }, + { + "prompt": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.214, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill", + "outline for a possible future skill" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.308, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document", + "document package", + "wiki" + ], + "no_build_directive": [ + "not an agent skill" + ] + } + } + } + }, + { + "prompt": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.375, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "workflow", + "checklist" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "no skill package", + "discussion only" + ] + } + } + } + } + ] + } + }, + "winner_blind_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "score": 0.38, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "score": 0.374, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "score": 0.084, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.136, + "negative_penalty": 0.18, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + } + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.054, + "support_score": 0, + "lexical_support": 0.154, + "negative_penalty": 0.22, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + } + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "score": 0.013, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.22, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + } + } + ] + } + }, + "current_blind_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "score": 0.38, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "score": 0.374, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "score": 0.084, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.136, + "negative_penalty": 0.18, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + } + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.054, + "support_score": 0, + "lexical_support": 0.154, + "negative_penalty": 0.22, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + } + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "score": 0.013, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.22, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + } + } + ] + } + }, + "baseline_blind_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "score": 0.393, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "score": 0.392, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "score": 0.019, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.189, + "support_score": 0.06, + "lexical_support": 0.136, + "negative_penalty": 0.18, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + } + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.0, + "support_score": 0.06, + "lexical_support": 0.0, + "negative_penalty": 0.22, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + } + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "score": 0.024, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.22, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + } + } + ] + } + }, + "winner_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 0, + "margin": 3, + "confidence": 0.74, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "multi_asset" + ], + "strong_positive_concepts": [ + "transform_workflow" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [ + "document_only" + ], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 1, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "prompts" + ], + "artifact_hits": [ + "prompts" + ], + "capability_overlap": 2, + "exclusive_negative_concepts": [ + "explain_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain", + "plain english" + ], + "positive_vote": 2, + "negative_vote": 3, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "multi_asset" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 2, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 2, + "margin": 1, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "current_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 0, + "margin": 3, + "confidence": 0.74, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "multi_asset" + ], + "strong_positive_concepts": [ + "transform_workflow" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [ + "document_only" + ], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 1, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "prompts" + ], + "artifact_hits": [ + "prompts" + ], + "capability_overlap": 2, + "exclusive_negative_concepts": [ + "explain_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain", + "plain english" + ], + "positive_vote": 2, + "negative_vote": 3, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "multi_asset" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 2, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 2, + "margin": 1, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "baseline_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 0, + "margin": 3, + "confidence": 0.74, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow" + ], + "strong_positive_concepts": [ + "transform_workflow" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [ + "document_only" + ], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 1, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "prompts" + ], + "artifact_hits": [ + "prompts" + ], + "capability_overlap": 2, + "exclusive_negative_concepts": [ + "explain_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain", + "plain english" + ], + "positive_vote": 2, + "negative_vote": 3, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 2, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 2, + "margin": 1, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "winner_adversarial_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_multi_asset_boundary": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_document_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_summary_translation_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_outline_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_brainstorm_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the noisy meeting chatter and operationalize this support escalation workflow into a maintained skill package with trigger hardening.", + "family": "adversarial_noisy_operationalize", + "score": 0.812, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "hardening" + ], + "reuse_package": [ + "package", + "maintained", + "operationalize" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this routine into a reusable capability from docs, transcripts, and prompt history, then stress test the route boundary.", + "family": "adversarial_multi_asset_boundary", + "score": 0.808, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.105, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this routine" + ], + "eval_optimize": [ + "stress test", + "route boundary" + ], + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "prompt history" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Create a wiki package from this runbook and do not generate any skill files.", + "family": "adversarial_document_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "document_only": [ + "wiki" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Summarize this SOP, translate it into Japanese for the knowledge base, and keep it as documentation only.", + "family": "adversarial_summary_translation_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive", + "summarize_only", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only", + "translate_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 1.25, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "sop" + ] + }, + "negative": { + "document_only": [ + "knowledge base" + ], + "no_build_directive": [ + "keep it as documentation only" + ], + "summarize_only": [ + "summarize" + ], + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Outline a possible future skill from these notes, but stop short of making a full skill and keep it as a plain note.", + "family": "adversarial_future_outline_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill" + ], + "no_build_directive": [ + "stop short of making a full skill", + "plain note", + "keep it as a plain note" + ] + } + } + } + }, + { + "prompt": "Brainstorm packaging improvements for later and keep this as discussion only.", + "family": "adversarial_brainstorm_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.88, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ], + "future_outline": [ + "for later" + ], + "no_build_directive": [ + "discussion only" + ] + } + } + } + } + ] + } + }, + "current_adversarial_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_multi_asset_boundary": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_document_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_summary_translation_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_outline_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_brainstorm_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the noisy meeting chatter and operationalize this support escalation workflow into a maintained skill package with trigger hardening.", + "family": "adversarial_noisy_operationalize", + "score": 0.812, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "hardening" + ], + "reuse_package": [ + "package", + "maintained", + "operationalize" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this routine into a reusable capability from docs, transcripts, and prompt history, then stress test the route boundary.", + "family": "adversarial_multi_asset_boundary", + "score": 0.808, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.105, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this routine" + ], + "eval_optimize": [ + "stress test", + "route boundary" + ], + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "prompt history" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Create a wiki package from this runbook and do not generate any skill files.", + "family": "adversarial_document_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "document_only": [ + "wiki" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Summarize this SOP, translate it into Japanese for the knowledge base, and keep it as documentation only.", + "family": "adversarial_summary_translation_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive", + "summarize_only", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only", + "translate_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 1.25, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "sop" + ] + }, + "negative": { + "document_only": [ + "knowledge base" + ], + "no_build_directive": [ + "keep it as documentation only" + ], + "summarize_only": [ + "summarize" + ], + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Outline a possible future skill from these notes, but stop short of making a full skill and keep it as a plain note.", + "family": "adversarial_future_outline_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill" + ], + "no_build_directive": [ + "stop short of making a full skill", + "plain note", + "keep it as a plain note" + ] + } + } + } + }, + { + "prompt": "Brainstorm packaging improvements for later and keep this as discussion only.", + "family": "adversarial_brainstorm_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.88, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ], + "future_outline": [ + "for later" + ], + "no_build_directive": [ + "discussion only" + ] + } + } + } + } + ] + } + }, + "baseline_adversarial_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_multi_asset_boundary": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_document_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_summary_translation_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_outline_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_brainstorm_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the noisy meeting chatter and operationalize this support escalation workflow into a maintained skill package with trigger hardening.", + "family": "adversarial_noisy_operationalize", + "score": 0.845, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.053, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "hardening" + ], + "reuse_package": [ + "package", + "maintained", + "operationalize" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this routine into a reusable capability from docs, transcripts, and prompt history, then stress test the route boundary.", + "family": "adversarial_multi_asset_boundary", + "score": 0.845, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.053, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this routine" + ], + "eval_optimize": [ + "stress test", + "route boundary" + ], + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "prompt history" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Create a wiki package from this runbook and do not generate any skill files.", + "family": "adversarial_document_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0.06, + "lexical_support": 0.143, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "document_only": [ + "wiki" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Summarize this SOP, translate it into Japanese for the knowledge base, and keep it as documentation only.", + "family": "adversarial_summary_translation_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive", + "summarize_only", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only", + "translate_only" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 1.25, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "sop" + ] + }, + "negative": { + "document_only": [ + "knowledge base" + ], + "no_build_directive": [ + "keep it as documentation only" + ], + "summarize_only": [ + "summarize" + ], + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Outline a possible future skill from these notes, but stop short of making a full skill and keep it as a plain note.", + "family": "adversarial_future_outline_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.05, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill" + ], + "no_build_directive": [ + "stop short of making a full skill", + "plain note", + "keep it as a plain note" + ] + } + } + } + }, + { + "prompt": "Brainstorm packaging improvements for later and keep this as discussion only.", + "family": "adversarial_brainstorm_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.88, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ], + "future_outline": [ + "for later" + ], + "no_build_directive": [ + "discussion only" + ] + } + } + } + } + ] + } + }, + "candidates": [ + { + "id": "current", + "label": "Current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "strategy": "current", + "estimated_tokens": 54, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 54 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "minimal", + "label": "Minimal", + "description": "Create, refactor, evaluate, and package agent skills. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "strategy": "minimal_template", + "estimated_tokens": 41, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 41 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "guardrail", + "label": "Guardrail", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "strategy": "guardrail_template", + "estimated_tokens": 56, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 56 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "balanced", + "label": "Balanced", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "strategy": "balanced_template", + "estimated_tokens": 60, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 60 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "artifact_aware", + "label": "Artifact Aware", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Trigger when requests mention workflows, prompts, transcripts, or docs and the job is to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "strategy": "artifact_template", + "estimated_tokens": 77, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 77 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "boundary", + "label": "Boundary", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "strategy": "boundary_template", + "estimated_tokens": 83, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 83 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + } + ], + "selection_logic": { + "priority": [ + "fewest false positives", + "fewest false negatives", + "highest near-neighbor pass rate", + "highest negative pass rate", + "highest precision", + "highest recall", + "shortest description" + ] + }, + "comparison": { + "winner_vs_current_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 1, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": -1, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 0.875, + "improved_recall": 1.0 + }, + "winner_vs_baseline_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + } + }, + "acceptance_gates": { + "selection_basis": "dev only", + "holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "current_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "baseline_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.707, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.416, + "max_non_trigger_score": 0.0, + "score_gap": 0.416, + "threshold_margin": 0.086, + "positive_threshold_buffer": 0.086, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "winner_family_health": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "current_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "baseline_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.393, + "mean_non_trigger_score": 0.011, + "mean_near_neighbor_score": 0.012, + "min_positive_score": 0.392, + "max_non_trigger_score": 0.024, + "score_gap": 0.368, + "threshold_margin": 0.062, + "positive_threshold_buffer": 0.062, + "negative_threshold_buffer": 0.306, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "judge_blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "winner_calibration": null, + "current_calibration": null, + "baseline_calibration": null, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "adversarial_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "current_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "baseline_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.845, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.845, + "max_non_trigger_score": 0.0, + "score_gap": 0.845, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.515, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + } + }, + "summary": { + "winner_label": "Current", + "winner_tokens": 54, + "current_tokens": 54, + "winner_dev_total_errors": 0, + "current_dev_total_errors": 0, + "winner_holdout_total_errors": 0, + "current_holdout_total_errors": 0, + "winner_blind_holdout_total_errors": 0, + "current_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_total_errors": 0, + "current_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_total_errors": 0, + "current_adversarial_holdout_total_errors": 0, + "winner_judge_blind_agreement_rate": 1.0, + "winner_adversarial_risk_band": "healthy", + "winner_adversarial_score_gap": 0.808, + "candidate_count": 6, + "baseline_tokens": 8, + "baseline_dev_total_errors": 1, + "baseline_holdout_total_errors": 0, + "baseline_blind_holdout_total_errors": 0, + "baseline_judge_blind_holdout_total_errors": 0, + "baseline_adversarial_holdout_total_errors": 0 + } +} diff --git a/reports/description_optimization.md b/reports/description_optimization.md new file mode 100644 index 0000000..fe74b1d --- /dev/null +++ b/reports/description_optimization.md @@ -0,0 +1,65 @@ +# Root Description Optimization + +Winner: `Current` + +- current tokens: `54` +- winner tokens: `54` +- baseline tokens: `8` + +## Winner + +Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. + +## Candidate Ranking + +| Candidate | Tokens | Dev FP | Dev FN | Dev Near | Holdout FP | Holdout FN | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| `Current` | 54 | 0 | 0 | 1.0 | 0 | 0 | +| `Minimal` | 41 | 1 | 1 | 1.0 | 0 | 0 | +| `Guardrail` | 56 | 1 | 1 | 1.0 | 0 | 0 | +| `Balanced` | 60 | 1 | 1 | 1.0 | 0 | 0 | +| `Artifact Aware` | 77 | 1 | 1 | 1.0 | 0 | 0 | +| `Boundary` | 83 | 1 | 1 | 1.0 | 0 | 0 | + +## Acceptance Gates + +| Gate | Winner FP | Winner FN | Current FP | Current FN | Baseline FP | Baseline FN | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Blind Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Judge Blind Holdout | 0 | 0 | 0 | 0 | 0 | 0 | +| Adversarial Holdout | 0 | 0 | 0 | 0 | 0 | 0 | + +## Calibration + +| Gate | Winner Gap | Winner Risk | Winner Boundary Rate | Current Gap | Baseline Gap | +| --- | ---: | --- | ---: | ---: | ---: | +| Holdout | 0.465 | healthy | 0.0 | 0.465 | 0.416 | +| Blind Holdout | 0.29 | watch | 0.333 | 0.29 | 0.368 | +| Adversarial Holdout | 0.808 | healthy | 0.0 | 0.808 | 0.845 | + +## Judge Blind Summary + +| Gate | Winner Agreement | Winner Mean Confidence | Current Agreement | Baseline Agreement | +| --- | ---: | ---: | ---: | ---: | +| Judge Blind Holdout | 1.0 | 0.66 | 1.0 | 1.0 | + +## Family Health + +| Gate | Winner Clean Families | Winner Weakest Family | Current Clean Families | Baseline Clean Families | +| --- | --- | --- | --- | --- | +| Holdout | 12/12 | workflow_to_skill (0 errors) | 12/12 | 12/12 | +| Blind Holdout | 6/6 | blind_operationalize (0 errors) | 6/6 | 6/6 | +| Judge Blind Holdout | 6/6 | blind_operationalize (0 errors) | 6/6 | 6/6 | +| Adversarial Holdout | 6/6 | adversarial_summary_translation_collision (0 errors) | 6/6 | 6/6 | + +## Selection Logic + +Ordered by: +- fewest false positives +- fewest false negatives +- highest near-neighbor pass rate +- highest negative pass rate +- highest precision +- highest recall +- shortest description diff --git a/reports/description_optimization_suite.json b/reports/description_optimization_suite.json new file mode 100644 index 0000000..29d8590 --- /dev/null +++ b/reports/description_optimization_suite.json @@ -0,0 +1,437 @@ +{ + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "winner_tokens": 54, + "current_tokens": 54, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "blind_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "adversarial_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.089, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.496, + "max_non_trigger_score": 0.211, + "score_gap": 0.285, + "threshold_margin": 0.016, + "positive_threshold_buffer": 0.016, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 1, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.45, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.037, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.057, + "score_gap": 0.023, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.423, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.82, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.645, + "max_non_trigger_score": 0.228, + "score_gap": 0.417, + "threshold_margin": 0.165, + "positive_threshold_buffer": 0.165, + "negative_threshold_buffer": 0.252, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.907, + "mean_non_trigger_score": 0.083, + "mean_near_neighbor_score": 0.139, + "min_positive_score": 0.815, + "max_non_trigger_score": 0.277, + "score_gap": 0.538, + "threshold_margin": 0.063, + "positive_threshold_buffer": 0.475, + "negative_threshold_buffer": 0.063, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true + } + ], + "ok": true +} diff --git a/reports/description_optimization_suite.md b/reports/description_optimization_suite.md new file mode 100644 index 0000000..8743457 --- /dev/null +++ b/reports/description_optimization_suite.md @@ -0,0 +1,15 @@ +# Description Optimization Suite + +| Target | Winner | Winner Tokens | Holdout FP | Holdout FN | Blind FP | Blind FN | Judge Blind Errors | Adv FP | Adv FN | Adv Gap | Adv Risk | Status | +| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- | +| `yao-meta-skill` | `Current` | 54 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.808 | healthy | ok | +| `team-frontend-review` | `Current` | 50 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.509 | healthy | ok | +| `governed-incident-command` | `Current` | 37 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.538 | watch | ok | + +## Family Coverage + +| Target | Blind Families | Judge Blind Families | Adversarial Families | +| --- | --- | --- | --- | +| `yao-meta-skill` | 6/6 clean; weakest=blind_operationalize | 6/6 clean; weakest=blind_operationalize | 6/6 clean; weakest=adversarial_summary_translation_collision | +| `team-frontend-review` | 6/6 clean; weakest=blind_premerge | 6/6 clean; weakest=blind_premerge | 6/6 clean; weakest=adversarial_quality_gate_review | +| `governed-incident-command` | 6/6 clean; weakest=blind_summary_only | 6/6 clean; weakest=blind_summary_only | 6/6 clean; weakest=adversarial_single_update_collision | diff --git a/reports/eval_suite.json b/reports/eval_suite.json new file mode 100644 index 0000000..d4c7f90 --- /dev/null +++ b/reports/eval_suite.json @@ -0,0 +1,3990 @@ +{ + "summary": { + "suite_count": 3, + "total_cases": 66, + "family_count": 21, + "false_positives": 0, + "false_negatives": 0, + "average_precision": 1.0, + "average_recall": 1.0 + }, + "family_summary": { + "workflow_to_skill": { + "total": 5, + "passed": 5, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 5, + "passed": 5, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "package_for_team": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "meta_skill_creation": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 5, + "passed": 5, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 4, + "passed": 4, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 5, + "passed": 5, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 4, + "passed": 4, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_only": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_vs_reusable": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_vs_build": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 4, + "passed": 4, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 4, + "passed": 4, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "complex_multi_asset": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 4, + "passed": 4, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "suites": { + "train": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 12, + "passed": 12, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 11, + "passed": 11, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 8, + "passed": 8, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "package_for_team": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "meta_skill_creation": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_only": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "one_off_vs_reusable": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_vs_build": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 3, + "passed": 3, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Create a skill from this repeated workflow.", + "family": "workflow_to_skill", + "score": 0.518, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.429, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "create a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this runbook into a reusable agent skill.", + "family": "workflow_to_skill", + "score": 0.688, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Convert this process note into a reusable skill package for the team.", + "family": "workflow_to_skill", + "score": 0.698, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.661, + "support_score": 0.04, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "reusable", + "package" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Improve this skill description and add evals.", + "family": "iterate_existing_skill", + "score": 0.337, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.286, + "support_score": 0, + "lexical_support": 0.429, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals" + ], + "iterate_existing_skill": [ + "this skill", + "skill description" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Refactor this prompt into a proper skill package.", + "family": "iterate_existing_skill", + "score": 0.504, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Package this skill for team reuse.", + "family": "package_for_team", + "score": 0.386, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.321, + "support_score": 0.04, + "lexical_support": 0.5, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill", + "package this skill" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Create a meta-skill for packaging internal workflows.", + "family": "meta_skill_creation", + "score": 0.534, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.482, + "support_score": 0.04, + "lexical_support": 0.5, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "meta skill", + "meta-skill" + ], + "reuse_package": [ + "packaging", + "internal workflows" + ], + "team_operationalize": [ + "internal" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Standardize this recurring workflow as a shareable skill package with references.", + "family": "package_for_team", + "score": 0.742, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.714, + "support_score": 0.04, + "lexical_support": 0.182, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "multi_asset": [ + "references" + ], + "reuse_package": [ + "package" + ], + "team_operationalize": [ + "standardize" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this recurring support escalation workflow into a maintained skill for the ops library.", + "family": "paraphrase_trigger", + "score": 0.38, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "ops library", + "maintained" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Operationalize this workflow as a reusable skill with validation notes and team library metadata.", + "family": "paraphrase_trigger", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0.04, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "operationalize this workflow as a reusable skill" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "team library", + "operationalize" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below are release notes, rough notes, and a transcript from the last handoff. Ignore the noise and turn this runbook into a reusable agent skill with references and scripts so the team can reuse it every quarter.", + "family": "long_context_trigger", + "score": 0.74, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.714, + "support_score": 0.04, + "lexical_support": 0.161, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "reuse", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "runbook", + "rough notes", + "transcript", + "release notes", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "We have a standard operating procedure, checklist, and prompt history scattered across docs. Formalize this process into a reusable capability, add validation, and package it for team reuse.", + "family": "long_context_trigger", + "score": 0.819, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0.04, + "lexical_support": 0.115, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "formalize this process into a reusable capability" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "process", + "checklist", + "prompt history", + "standard operating procedure", + "operating procedure" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Explain what a workflow is.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "explain_only": [ + "explain what" + ] + } + } + } + }, + { + "prompt": "Just explain what a skill is.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.167, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "explain what", + "just explain" + ] + } + } + } + }, + { + "prompt": "Summarize this random note.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "Translate this README into Japanese.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.53, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "document_only": [ + "readme" + ], + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + }, + { + "prompt": "Write a product headline for this landing page.", + "family": "document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0.18, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "document_only": [ + "headline", + "landing page" + ] + } + } + } + }, + { + "prompt": "Draft a blog title for this article.", + "family": "document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.18, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "document_only": [ + "article", + "blog title" + ] + } + } + } + }, + { + "prompt": "Rewrite this paragraph more clearly, but do not package anything.", + "family": "document_only", + "score": 0.172, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Explain the difference between a runbook and a workflow.", + "family": "explain_only", + "score": 0.164, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow", + "runbook" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a long SOP, but I only want a summary for leadership and a recap of the main points. Do not build anything reusable.", + "family": "long_context_summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.087, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "sop" + ] + }, + "negative": { + "no_build_directive": [ + "do not build" + ], + "summarize_only": [ + "summary", + "only want a summary", + "recap" + ] + } + } + } + }, + { + "prompt": "Keep this as documentation only for the knowledge base and rewrite it as a reference document.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.067, + "negative_penalty": 0.18, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "document_only": [ + "document", + "reference document", + "knowledge base" + ] + } + } + } + }, + { + "prompt": "Translate this long onboarding flow into French and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.176, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "onboarding flow" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into french" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Create a one-off prompt for this task.", + "family": "one_off_vs_reusable", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "one_off_request" + ], + "exclusive_negative_concepts": [ + "one_off_request" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.25, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "one_off_request": [ + "one-off" + ] + } + } + } + }, + { + "prompt": "Write a custom answer for this request without creating a skill package.", + "family": "one_off_vs_reusable", + "score": 0.115, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "one_off_request" + ], + "exclusive_negative_concepts": [ + "one_off_request" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.273, + "negative_penalty": 0.24, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "one_off_request": [ + "custom answer", + "for this request" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm process ideas without building a skill.", + "family": "brainstorm_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.111, + "negative_penalty": 0.66, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "ideas" + ], + "no_build_directive": [ + "without building" + ] + } + } + } + }, + { + "prompt": "Improve this README but do not turn it into a skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.6, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "document_only": [ + "readme" + ], + "no_build_directive": [ + "do not turn it into a skill" + ] + } + } + } + }, + { + "prompt": "Make a checklist for this task, but not a reusable skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.42, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ] + } + } + } + }, + { + "prompt": "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.42, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "plain note", + "keep it as a plain note" + ] + } + } + } + }, + { + "prompt": "We may convert this workflow later, but for now keep it as documentation only and produce a reference document.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.053, + "negative_penalty": 0.6, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "reference document" + ], + "no_build_directive": [ + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "Draft a template only from this process and stop short of making a full skill package.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + } + ] + }, + "comparison": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "returncode": 0 + }, + "dev": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 8, + "passed": 8, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "complex_multi_asset": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill", + "score": 0.379, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist", + "operations checklist" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill", + "score": 0.333, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.286, + "support_score": 0.04, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "trigger evals" + ], + "iterate_existing_skill": [ + "this skill", + "sharing it with the team", + "before sharing it with the team" + ], + "team_operationalize": [ + "team", + "sharing it with the team" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset", + "score": 0.816, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.208, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package", + "turn all of that into one reusable skill package" + ], + "eval_optimize": [ + "evals", + "packaging checks" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "runbook", + "process", + "prompt history" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset", + "score": 0.454, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "production-ready" + ], + "transform_workflow": [ + "workflow", + "workflow fragments", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill", + "score": 0.514, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.464, + "support_score": 0.04, + "lexical_support": 0.214, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "distribution" + ], + "team_operationalize": [ + "team", + "distribution" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger", + "score": 0.39, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0.04, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "team library", + "maintained" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0.04, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this workflow" + ], + "eval_optimize": [ + "validate", + "route boundary" + ], + "reuse_package": [ + "package", + "distribution", + "shared capability" + ], + "team_operationalize": [ + "shared", + "distribution" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger", + "score": 0.871, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0.04, + "lexical_support": 0.154, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill", + "skill package" + ], + "eval_optimize": [ + "validation" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "rough notes", + "transcript", + "onboarding flow", + "scripts" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Give me ideas for improving our process.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "ideas" + ] + } + } + } + }, + { + "prompt": "Review this note and tell me what it means in plain English.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "tell me what it means", + "plain english" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.133, + "negative_penalty": 0.66, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "improve packaging" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "translate_only": [ + "translate", + "into french" + ] + } + } + } + }, + { + "prompt": "Summarize this workflow and list the main points only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.68, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "explain_only": [ + "explain it", + "in simpler language" + ], + "no_build_directive": [ + "do not convert it into a reusable capability", + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only", + "score": 0.236, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0.3, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "summarize_only": [ + "summary", + "recap" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "explain_only": [ + "explain it" + ], + "no_build_directive": [ + "no packaging needed" + ] + } + } + } + }, + { + "prompt": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.42, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + }, + { + "prompt": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document" + ], + "no_build_directive": [ + "not as an agent skill" + ] + } + } + } + }, + { + "prompt": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.321, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "document_only": [ + "document", + "reusable-looking document" + ], + "no_build_directive": [ + "without turning them into an agent skill" + ] + } + } + } + }, + { + "prompt": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.048, + "negative_penalty": 0.82, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document", + "template only" + ], + "future_outline": [ + "not now", + "later but not now" + ], + "no_build_directive": [ + "not now", + "later but not now" + ] + } + } + } + }, + { + "prompt": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "for later", + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + } + ] + }, + "comparison": { + "baseline_false_positives": 0, + "baseline_false_negatives": 1, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": -1, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 0.875, + "improved_recall": 1.0 + }, + "returncode": 0 + }, + "holdout": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 5, + "passed": 5, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + } + }, + "family_stats": { + "complex_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset", + "score": 0.465, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0.04, + "lexical_support": 0.211, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable" + ], + "team_operationalize": [ + "internal" + ], + "transform_workflow": [ + "process", + "internal process note", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill", + "score": 0.684, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.643, + "support_score": 0.04, + "lexical_support": 0.278, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "near-neighbor", + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "package", + "distribution" + ], + "team_operationalize": [ + "distribution" + ], + "transform_workflow": [ + "existing skill draft" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill", + "score": 0.805, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill library entry" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "library entry" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger", + "score": 0.874, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "packaging checks" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "quarterly release routine", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger", + "score": 0.691, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.661, + "support_score": 0.04, + "lexical_support": 0.16, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "turn this playbook into an agent capability package", + "agent capability" + ], + "reuse_package": [ + "package", + "team library", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "prompt history", + "playbook", + "transcript", + "release notes" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ], + "summarize_only": [ + "summary", + "only want a summary" + ] + } + } + } + }, + { + "prompt": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package", + "release" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "explain_only": [ + "explain this", + "in simpler language" + ], + "no_build_directive": [ + "do not package it" + ] + } + } + } + }, + { + "prompt": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.167, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "release" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document" + ], + "no_build_directive": [ + "not for agent execution" + ] + } + } + } + }, + { + "prompt": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.188, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into russian" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "semantic_coverage": 0.304, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.46, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ] + }, + "negative": { + "brainstorm_only": [ + "shape an idea" + ], + "future_outline": [ + "before we decide whether to build" + ] + } + } + } + }, + { + "prompt": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.214, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill", + "outline for a possible future skill" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.308, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document", + "document package", + "wiki" + ], + "no_build_directive": [ + "not an agent skill" + ] + } + } + } + }, + { + "prompt": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.375, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "workflow", + "checklist" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "no skill package", + "discussion only" + ] + } + } + } + } + ] + }, + "comparison": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "returncode": 0 + } + } +} diff --git a/reports/evidence_consistency.json b/reports/evidence_consistency.json new file mode 100644 index 0000000..1468539 --- /dev/null +++ b/reports/evidence_consistency.json @@ -0,0 +1,3609 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "check_count": 39, + "pass_count": 39, + "warn_count": 0, + "fail_count": 0, + "decision": "consistent" + }, + "status_counts": { + "pass": 39, + "warn": 0, + "fail": 0 + }, + "checks": [ + { + "key": "required-report-artifacts", + "label": "Required report artifacts are readable", + "status": "pass", + "expected": "all required JSON and Markdown reports exist and parse", + "actual": "all readable", + "paths": [ + "reports/benchmark_reproducibility.json", + "reports/skill-overview.json", + "reports/skill-interpretation.json", + "reports/adoption_drift_report.json", + "reports/world_class_evidence_ledger.json", + "reports/world_class_evidence_plan.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_preflight.json", + "reports/world_class_submission_review.json", + "reports/world_class_operator_runbook.json", + "reports/skill_os2_coverage.json", + "reports/review-studio.json", + "reports/package_verification.json", + "reports/install_simulation.json", + "reports/security_trust_report.json", + "reports/context_budget.json", + "reports/world_class_claim_guard.json", + "reports/skill-os-2-review.md" + ], + "detail": "The consistency gate can only be trusted when every source JSON report parses and every source Markdown report is readable." + }, + { + "key": "release-evidence-flow-covers-first-class-reports", + "label": "Release evidence flow covers first-class reports", + "status": "pass", + "expected": { + "AGENTS.md": true, + "source_refresh_section": true, + "clean_lock_section": true, + "source_refresh_commands": { + "python3 scripts/run_output_execution.py --runner-command '[\"python3\",\"scripts/local_output_eval_runner.py\"]'": true, + "python3 scripts/compile_skill.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/cross_packager.py . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip": true, + "python3 scripts/simulate_install.py . --package-dir dist --install-root dist/install-simulation --output-json reports/install_simulation.json --output-md reports/install_simulation.md --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/trust_check.py . --output-json reports/security_trust_report.json --output-md reports/security_trust_report.md": true, + "python3 scripts/registry_audit.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/verify_package.py . --package-dir dist --expectations evals/packaging_expectations.json --registry-json reports/registry_audit.json --output-json reports/package_verification.json --output-md reports/package_verification.md --require-zip --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json --current-package-json reports/registry_audit.json --output-json reports/upgrade_check.json --output-md reports/upgrade_check.md --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_adoption_drift_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_architecture_maintainability.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/python_compat_check.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/probe_runtime_permissions.py . --package-dir dist": true, + "python3 scripts/render_review_waivers.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_review_annotations.py .": true, + "python3 scripts/build_skill_atlas.py --workspace-root . --output-dir skill_atlas --report-html reports/skill_atlas.html --report-json reports/skill_atlas.json --today \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_evidence_plan.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_evidence_ledger.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_evidence_intake.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_submission_review.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_operator_runbook.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_claim_guard.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_daily_skillops_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_weekly_curator_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_os2_audit.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_os2_coverage.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_context_reports.py --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_benchmark_reproducibility.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_overview.py .": true, + "python3 scripts/render_skill_interpretation.py .": true, + "python3 scripts/render_review_viewer.py .": true, + "python3 scripts/render_world_class_preflight.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json": true, + "python3 scripts/render_evidence_consistency.py . --generated-at \"$GENERATED_AT\"": true + }, + "clean_lock_commands": { + "python3 scripts/render_context_reports.py --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_benchmark_reproducibility.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_daily_skillops_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_weekly_curator_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_overview.py .": true, + "python3 scripts/render_skill_interpretation.py .": true, + "python3 scripts/render_review_viewer.py .": true, + "python3 scripts/render_world_class_preflight.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json": true, + "python3 scripts/render_evidence_consistency.py . --generated-at \"$GENERATED_AT\"": true + } + }, + "actual": { + "AGENTS.md": true, + "source_refresh_section": true, + "clean_lock_section": true, + "source_refresh_commands": { + "python3 scripts/run_output_execution.py --runner-command '[\"python3\",\"scripts/local_output_eval_runner.py\"]'": true, + "python3 scripts/compile_skill.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/cross_packager.py . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip": true, + "python3 scripts/simulate_install.py . --package-dir dist --install-root dist/install-simulation --output-json reports/install_simulation.json --output-md reports/install_simulation.md --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/trust_check.py . --output-json reports/security_trust_report.json --output-md reports/security_trust_report.md": true, + "python3 scripts/registry_audit.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/verify_package.py . --package-dir dist --expectations evals/packaging_expectations.json --registry-json reports/registry_audit.json --output-json reports/package_verification.json --output-md reports/package_verification.md --require-zip --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json --current-package-json reports/registry_audit.json --output-json reports/upgrade_check.json --output-md reports/upgrade_check.md --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_adoption_drift_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_architecture_maintainability.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/python_compat_check.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/probe_runtime_permissions.py . --package-dir dist": true, + "python3 scripts/render_review_waivers.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_review_annotations.py .": true, + "python3 scripts/build_skill_atlas.py --workspace-root . --output-dir skill_atlas --report-html reports/skill_atlas.html --report-json reports/skill_atlas.json --today \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_evidence_plan.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_evidence_ledger.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_evidence_intake.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_submission_review.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_operator_runbook.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_world_class_claim_guard.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_daily_skillops_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_weekly_curator_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_os2_audit.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_os2_coverage.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_context_reports.py --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_benchmark_reproducibility.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_overview.py .": true, + "python3 scripts/render_skill_interpretation.py .": true, + "python3 scripts/render_review_viewer.py .": true, + "python3 scripts/render_world_class_preflight.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json": true, + "python3 scripts/render_evidence_consistency.py . --generated-at \"$GENERATED_AT\"": true + }, + "clean_lock_commands": { + "python3 scripts/render_context_reports.py --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_benchmark_reproducibility.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_daily_skillops_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_weekly_curator_report.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_skill_overview.py .": true, + "python3 scripts/render_skill_interpretation.py .": true, + "python3 scripts/render_review_viewer.py .": true, + "python3 scripts/render_world_class_preflight.py . --generated-at \"$GENERATED_AT\"": true, + "python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json": true, + "python3 scripts/render_evidence_consistency.py . --generated-at \"$GENERATED_AT\"": true + } + }, + "paths": [ + "AGENTS.md", + "reports/output_execution_runs.json", + "reports/install_simulation.json", + "reports/security_trust_report.json", + "reports/registry_audit.json", + "reports/package_verification.json", + "reports/upgrade_check.json", + "reports/adoption_drift_report.json", + "reports/architecture_maintainability.json", + "reports/python_compatibility.json", + "reports/runtime_permission_probes.json", + "reports/review_waivers.json", + "reports/review_annotations.json", + "reports/skill_atlas.json", + "reports/skill_os2_audit.json", + "reports/skill_os2_coverage.json", + "reports/context_budget.json", + "reports/context_budget_summary.json", + "reports/benchmark_reproducibility.json", + "reports/skill-overview.json", + "reports/skill-interpretation.json", + "reports/review-viewer.json", + "reports/world_class_evidence_preflight.json", + "reports/skillops/daily", + "reports/skillops/weekly", + "reports/review-studio.json", + "reports/evidence_consistency.json" + ], + "detail": "Release refresh and clean-lock instructions must regenerate every first-class report before evidence consistency can be trusted." + }, + { + "key": "review-studio-gate-action-mirror", + "label": "Review Studio gates mirror review actions", + "status": "pass", + "expected": { + "non_pass_gate_keys": [ + "output-lab", + "review-waivers", + "world-class-evidence" + ], + "action_gate_keys": [ + "output-lab", + "review-waivers", + "world-class-evidence" + ], + "pass_gate_action_ids_empty": true, + "gate_action_mirrors": { + "output-lab": { + "review_action_id": "review-action-output-lab", + "review_action_status": "warn", + "title_present": true, + "next_step_present": true, + "reason_present": true, + "source_ref_present": true, + "verification_command_present": true, + "top_level_action_id": "review-action-output-lab", + "top_level_action_status": "warn", + "top_level_title_present": true, + "top_level_next_step_present": true, + "top_level_reason_present": true, + "top_level_source_ref_present": true, + "top_level_verification_command_present": true + }, + "review-waivers": { + "review_action_id": "review-action-review-waivers", + "review_action_status": "warn", + "title_present": true, + "next_step_present": true, + "reason_present": true, + "source_ref_present": true, + "verification_command_present": true, + "top_level_action_id": "review-action-review-waivers", + "top_level_action_status": "warn", + "top_level_title_present": true, + "top_level_next_step_present": true, + "top_level_reason_present": true, + "top_level_source_ref_present": true, + "top_level_verification_command_present": true + }, + "world-class-evidence": { + "review_action_id": "review-action-world-class-evidence", + "review_action_status": "warn", + "title_present": true, + "next_step_present": true, + "reason_present": true, + "source_ref_present": true, + "verification_command_present": true, + "top_level_action_id": "review-action-world-class-evidence", + "top_level_action_status": "warn", + "top_level_title_present": true, + "top_level_next_step_present": true, + "top_level_reason_present": true, + "top_level_source_ref_present": true, + "top_level_verification_command_present": true + } + } + }, + "actual": { + "non_pass_gate_keys": [ + "output-lab", + "review-waivers", + "world-class-evidence" + ], + "action_gate_keys": [ + "output-lab", + "review-waivers", + "world-class-evidence" + ], + "pass_gate_action_ids_empty": true, + "gate_action_mirrors": { + "output-lab": { + "review_action_id": "review-action-output-lab", + "review_action_status": "warn", + "title_present": true, + "next_step_present": true, + "reason_present": true, + "source_ref_present": true, + "verification_command_present": true, + "top_level_action_id": "review-action-output-lab", + "top_level_action_status": "warn", + "top_level_title_present": true, + "top_level_next_step_present": true, + "top_level_reason_present": true, + "top_level_source_ref_present": true, + "top_level_verification_command_present": true + }, + "review-waivers": { + "review_action_id": "review-action-review-waivers", + "review_action_status": "warn", + "title_present": true, + "next_step_present": true, + "reason_present": true, + "source_ref_present": true, + "verification_command_present": true, + "top_level_action_id": "review-action-review-waivers", + "top_level_action_status": "warn", + "top_level_title_present": true, + "top_level_next_step_present": true, + "top_level_reason_present": true, + "top_level_source_ref_present": true, + "top_level_verification_command_present": true + }, + "world-class-evidence": { + "review_action_id": "review-action-world-class-evidence", + "review_action_status": "warn", + "title_present": true, + "next_step_present": true, + "reason_present": true, + "source_ref_present": true, + "verification_command_present": true, + "top_level_action_id": "review-action-world-class-evidence", + "top_level_action_status": "warn", + "top_level_title_present": true, + "top_level_next_step_present": true, + "top_level_reason_present": true, + "top_level_source_ref_present": true, + "top_level_verification_command_present": true + } + } + }, + "paths": [ + "reports/review-studio.json" + ], + "detail": "Every non-pass Review Studio gate must have exactly one top-level review action and a gate-local action summary with source refs and a verification command." + }, + { + "key": "review-studio-context-budget-mirror", + "label": "Review Studio mirrors context budget governance", + "status": "pass", + "expected": { + "status": "pass", + "detail": "initial load 987/1000; deferred 531767/120000; top deferred scripts 469568; resource governance governed; quality density 141.8", + "evidence": "reports/context_budget.json" + }, + "actual": { + "status": "pass", + "detail": "initial load 987/1000; deferred 531767/120000; top deferred scripts 469568; resource governance governed; quality density 141.8", + "evidence": "reports/context_budget.json" + }, + "paths": [ + "reports/context_budget.json", + "reports/review-studio.json" + ], + "detail": "Review Studio must not keep stale context warnings after context reports prove large deferred resources are governed." + }, + { + "key": "benchmark-release-lock-self-consistency", + "label": "Benchmark release lock matches source dirty state", + "status": "pass", + "expected": false, + "actual": false, + "paths": [ + "reports/benchmark_reproducibility.json" + ], + "detail": "The benchmark release lock must be blocked by source changes, while generated evidence artifacts are tracked as generation context." + }, + { + "key": "benchmark-clean-worktree-release-lock", + "label": "Clean worktree keeps a clean benchmark release lock", + "status": "pass", + "expected": "checked only when git is available and the current worktree is clean", + "actual": { + "available": true, + "clean": false, + "changed_file_count": 39 + }, + "paths": [ + "reports/benchmark_reproducibility.json" + ], + "detail": "Dirty or non-git worktrees cannot prove final release-lock freshness, so this check is advisory until the final clean-lock pass." + }, + { + "key": "skill-ir-evidence-path-contract", + "label": "Human-facing reports expose the canonical Skill IR artifact", + "status": "pass", + "expected": { + "overview_source_path": "skill-ir/examples/yao-meta-skill.json", + "interpretation_source_path": "skill-ir/examples/yao-meta-skill.json", + "review_studio_evidence_path": "skill-ir/examples/yao-meta-skill.json", + "overview_deliverable": true, + "interpretation_deliverable": true, + "exists": true, + "schema_version": "2.0.0" + }, + "actual": { + "overview_source_path": "skill-ir/examples/yao-meta-skill.json", + "interpretation_source_path": "skill-ir/examples/yao-meta-skill.json", + "review_studio_evidence_path": "skill-ir/examples/yao-meta-skill.json", + "overview_deliverable": true, + "interpretation_deliverable": true, + "exists": true, + "schema_version": "2.0.0" + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json", + "reports/review-studio.json", + "skill-ir/examples/yao-meta-skill.json" + ], + "detail": "Skill IR is the 2.0 platform-neutral semantic source, so user-facing reports must link to the artifact that actually exists." + }, + { + "key": "overview-benchmark-commit", + "label": "overview embeds the benchmark commit", + "status": "pass", + "expected": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "actual": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "paths": [ + "reports/benchmark_reproducibility.json", + "reports/skill-overview.json" + ], + "detail": "Human-facing reports must point to the same benchmark release-lock commit." + }, + { + "key": "overview-benchmark-summary", + "label": "overview embeds benchmark summary fields", + "status": "pass", + "expected": { + "release_lock_ready": false, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "source_contract_sha256": "b5cfa61c532d9ecd55d4d3a91aaf249bab0393251319a0f391422edb07a9e658", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4 + }, + "actual": { + "release_lock_ready": false, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "source_contract_sha256": "b5cfa61c532d9ecd55d4d3a91aaf249bab0393251319a0f391422edb07a9e658", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4 + }, + "paths": [ + "reports/benchmark_reproducibility.json", + "reports/skill-overview.json" + ], + "detail": "Selected summary fields must match exactly across generated reports." + }, + { + "key": "overview-adoption-summary", + "label": "overview embeds adoption drift summary fields", + "status": "pass", + "expected": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "adoption_rate": 100.0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "source_types": { + "manual": 1 + } + }, + "actual": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "adoption_rate": 100.0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "source_types": { + "manual": 1 + } + }, + "paths": [ + "reports/adoption_drift_report.json", + "reports/skill-overview.json" + ], + "detail": "Selected summary fields must match exactly across generated reports." + }, + { + "key": "overview-world-class-ledger-summary", + "label": "overview embeds world-class ledger summary fields", + "status": "pass", + "expected": { + "ledger_entry_count": 4, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "actual": { + "ledger_entry_count": 4, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/skill-overview.json" + ], + "detail": "Selected summary fields must match exactly across generated reports." + }, + { + "key": "overview-world-class-readiness", + "label": "overview derives readiness from the ledger", + "status": "pass", + "expected": { + "ready": false, + "decision": "evidence-pending", + "pending_count": 4, + "accepted_count": 0, + "source_check_count": 19, + "source_pass_count": 12 + }, + "actual": { + "ready": false, + "decision": "evidence-pending", + "pending_count": 4, + "accepted_count": 0, + "source_check_count": 19, + "source_pass_count": 12 + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/skill-overview.json" + ], + "detail": "Readiness summaries must be derived from the evidence ledger, not hand-maintained copy." + }, + { + "key": "interpretation-benchmark-commit", + "label": "interpretation embeds the benchmark commit", + "status": "pass", + "expected": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "actual": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "paths": [ + "reports/benchmark_reproducibility.json", + "reports/skill-interpretation.json" + ], + "detail": "Human-facing reports must point to the same benchmark release-lock commit." + }, + { + "key": "interpretation-benchmark-summary", + "label": "interpretation embeds benchmark summary fields", + "status": "pass", + "expected": { + "release_lock_ready": false, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "source_contract_sha256": "b5cfa61c532d9ecd55d4d3a91aaf249bab0393251319a0f391422edb07a9e658", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4 + }, + "actual": { + "release_lock_ready": false, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "source_contract_sha256": "b5cfa61c532d9ecd55d4d3a91aaf249bab0393251319a0f391422edb07a9e658", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4 + }, + "paths": [ + "reports/benchmark_reproducibility.json", + "reports/skill-interpretation.json" + ], + "detail": "Selected summary fields must match exactly across generated reports." + }, + { + "key": "interpretation-adoption-summary", + "label": "interpretation embeds adoption drift summary fields", + "status": "pass", + "expected": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "adoption_rate": 100.0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "source_types": { + "manual": 1 + } + }, + "actual": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "adoption_rate": 100.0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "source_types": { + "manual": 1 + } + }, + "paths": [ + "reports/adoption_drift_report.json", + "reports/skill-interpretation.json" + ], + "detail": "Selected summary fields must match exactly across generated reports." + }, + { + "key": "interpretation-world-class-ledger-summary", + "label": "interpretation embeds world-class ledger summary fields", + "status": "pass", + "expected": { + "ledger_entry_count": 4, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "actual": { + "ledger_entry_count": 4, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/skill-interpretation.json" + ], + "detail": "Selected summary fields must match exactly across generated reports." + }, + { + "key": "interpretation-world-class-readiness", + "label": "interpretation derives readiness from the ledger", + "status": "pass", + "expected": { + "ready": false, + "decision": "evidence-pending", + "pending_count": 4, + "accepted_count": 0, + "source_check_count": 19, + "source_pass_count": 12 + }, + "actual": { + "ready": false, + "decision": "evidence-pending", + "pending_count": 4, + "accepted_count": 0, + "source_check_count": 19, + "source_pass_count": 12 + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/skill-interpretation.json" + ], + "detail": "Readiness summaries must be derived from the evidence ledger, not hand-maintained copy." + }, + { + "key": "overview-interpretation-lockstep-scorecard", + "label": "Overview and interpretation share scorecard", + "status": "pass", + "expected": { + "completeness_score": { + "label": "完整度", + "score": 100, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 100, + "reasons": [ + "已生成 20 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 100, + "reasons": [ + "SKILL.md 约 314 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 已承载确定性逻辑。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 42, + "reasons": [ + "入口约 314 个词/字,references 约 16934 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本偏高,建议压缩入口或拆分 references。" + ] + } + }, + "actual": { + "completeness_score": { + "label": "完整度", + "score": 100, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 100, + "reasons": [ + "已生成 20 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 100, + "reasons": [ + "SKILL.md 约 314 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 已承载确定性逻辑。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 42, + "reasons": [ + "入口约 314 个词/字,references 约 16934 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本偏高,建议压缩入口或拆分 references。" + ] + } + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-capability-profile", + "label": "Overview and interpretation share capability_profile", + "status": "pass", + "expected": { + "archetype": "governed", + "task_family": "Execution operation", + "maturity": "governed", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "actual": { + "archetype": "governed", + "task_family": "Execution operation", + "maturity": "governed", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-principle-model", + "label": "Overview and interpretation share principle_model", + "status": "pass", + "expected": { + "nodes": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "actual": { + "nodes": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-contract-boundary", + "label": "Overview and interpretation share contract_boundary", + "status": "pass", + "expected": { + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "actual": { + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-quality-review", + "label": "Overview and interpretation share quality_review", + "status": "pass", + "expected": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "gaps": [ + "上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + } + }, + "actual": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "gaps": [ + "上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + } + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-risk-governance", + "label": "Overview and interpretation share risk_governance", + "status": "pass", + "expected": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 1, + "signal": "SKILL.md 约 314 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "actual": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 1, + "signal": "SKILL.md 约 314 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-world-class-readiness", + "label": "Overview and interpretation share world_class_readiness", + "status": "pass", + "expected": { + "ready": false, + "decision": "evidence-pending", + "entry_count": 4, + "pending_count": 4, + "accepted_count": 0, + "external_pending_count": 3, + "human_pending_count": 1, + "source_check_count": 19, + "source_pass_count": 12, + "conclusion_zh": "世界级证据尚未完成:4 项待补,0 项已接受。", + "conclusion_en": "World-class evidence is not complete: 4 pending, 0 accepted.", + "entries": [ + { + "key": "provider-holdout", + "label_zh": "提供商留出", + "label_en": "Provider Holdout", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "缺少真实 provider 模型运行和 token metadata。", + "summary_en": "Missing a real provider model run and token metadata.", + "blocked_checks": [] + }, + { + "key": "human-adjudication", + "label_zh": "人工盲评", + "label_en": "Human Adjudication", + "category": "human", + "category_zh": "人工证据", + "category_en": "Human evidence", + "status": "pending", + "summary_zh": "盲评 pair 仍待真实 reviewer 决策。", + "summary_en": "Blind-review pairs still need real reviewer decisions.", + "blocked_checks": [ + "No pending decisions", + "Judgments complete", + "Reviewer metadata" + ] + }, + { + "key": "native-permission-enforcement", + "label_zh": "原生权限", + "label_en": "Native Permission", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "原生 runtime enforcement 仍待目标客户端或外部安装器证明。", + "summary_en": "Native runtime enforcement still needs target-client or external-installer proof.", + "blocked_checks": [ + "Native enforcement" + ] + }, + { + "key": "native-client-telemetry", + "label_zh": "原生遥测", + "label_en": "Native Telemetry", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "真实外部客户端 metadata-only 事件仍未导入。", + "summary_en": "Real external-client metadata-only events have not been imported yet.", + "blocked_checks": [ + "External events" + ] + } + ] + }, + "actual": { + "ready": false, + "decision": "evidence-pending", + "entry_count": 4, + "pending_count": 4, + "accepted_count": 0, + "external_pending_count": 3, + "human_pending_count": 1, + "source_check_count": 19, + "source_pass_count": 12, + "conclusion_zh": "世界级证据尚未完成:4 项待补,0 项已接受。", + "conclusion_en": "World-class evidence is not complete: 4 pending, 0 accepted.", + "entries": [ + { + "key": "provider-holdout", + "label_zh": "提供商留出", + "label_en": "Provider Holdout", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "缺少真实 provider 模型运行和 token metadata。", + "summary_en": "Missing a real provider model run and token metadata.", + "blocked_checks": [] + }, + { + "key": "human-adjudication", + "label_zh": "人工盲评", + "label_en": "Human Adjudication", + "category": "human", + "category_zh": "人工证据", + "category_en": "Human evidence", + "status": "pending", + "summary_zh": "盲评 pair 仍待真实 reviewer 决策。", + "summary_en": "Blind-review pairs still need real reviewer decisions.", + "blocked_checks": [ + "No pending decisions", + "Judgments complete", + "Reviewer metadata" + ] + }, + { + "key": "native-permission-enforcement", + "label_zh": "原生权限", + "label_en": "Native Permission", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "原生 runtime enforcement 仍待目标客户端或外部安装器证明。", + "summary_en": "Native runtime enforcement still needs target-client or external-installer proof.", + "blocked_checks": [ + "Native enforcement" + ] + }, + { + "key": "native-client-telemetry", + "label_zh": "原生遥测", + "label_en": "Native Telemetry", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "真实外部客户端 metadata-only 事件仍未导入。", + "summary_en": "Real external-client metadata-only events have not been imported yet.", + "blocked_checks": [ + "External events" + ] + } + ] + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-package-assets", + "label": "Overview and interpretation share package_assets", + "status": "pass", + "expected": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "file_count": 393, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 35 + }, + { + "label": "scripts", + "value": 155 + }, + { + "label": "evals", + "value": 29 + }, + { + "label": "reports", + "value": 170 + } + ] + }, + "actual": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "file_count": 393, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 35 + }, + { + "label": "scripts", + "value": 155 + }, + { + "label": "evals", + "value": 29 + }, + { + "label": "reports", + "value": 170 + } + ] + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-interpretation-lockstep-iteration-roadmap", + "label": "Overview and interpretation share iteration_roadmap", + "status": "pass", + "expected": { + "items": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ] + }, + "actual": { + "items": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ] + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-interpretation.json" + ], + "detail": "The first-class interpretation report must stay in lockstep with the canonical overview model." + }, + { + "key": "overview-html-contract", + "label": "overview has a stable HTML contract", + "status": "pass", + "expected": { + "schema_version": "2.0", + "default_language": "zh-CN", + "layout": "kami-white-audit-v2", + "html_report": "reports/skill-overview.html", + "html_exists": true + }, + "actual": { + "schema_version": "2.0", + "default_language": "zh-CN", + "layout": "kami-white-audit-v2", + "html_report": "reports/skill-overview.html", + "html_exists": true + }, + "paths": [ + "reports/skill-overview.json", + "reports/skill-overview.html" + ], + "detail": "Report output paths and language defaults are part of the user-facing contract." + }, + { + "key": "interpretation-html-contract", + "label": "interpretation has a stable HTML contract", + "status": "pass", + "expected": { + "schema_version": "2.0", + "default_language": "zh-CN", + "layout": "kami-white-audit-v2", + "html_report": "reports/skill-interpretation.html", + "html_exists": true + }, + "actual": { + "schema_version": "2.0", + "default_language": "zh-CN", + "layout": "kami-white-audit-v2", + "html_report": "reports/skill-interpretation.html", + "html_exists": true + }, + "paths": [ + "reports/skill-interpretation.json", + "reports/skill-interpretation.html" + ], + "detail": "Report output paths and language defaults are part of the user-facing contract." + }, + { + "key": "coverage-world-class-boundary", + "label": "Coverage report mirrors world-class evidence boundary", + "status": "pass", + "expected": { + "world_class_evidence_pending_count": 4, + "public_world_class_ready": false + }, + "actual": { + "world_class_evidence_pending_count": 4, + "public_world_class_ready": false + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/skill_os2_coverage.json" + ], + "detail": "Blueprint coverage can be locally complete while public world-class evidence remains pending." + }, + { + "key": "benchmark-world-class-boundary", + "label": "Benchmark report mirrors world-class evidence boundary", + "status": "pass", + "expected": { + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "public_claim_ready": false + }, + "actual": { + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "public_claim_ready": false + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/benchmark_reproducibility.json" + ], + "detail": "Benchmark reproducibility must not overstate public claim readiness." + }, + { + "key": "benchmark-beta-public-claim-split", + "label": "Benchmark separates beta testing from public claims", + "status": "pass", + "expected": { + "beta_test_ready": false, + "public_claim_ready": false, + "human_review_complete": false, + "beta_release_ready": false, + "beta_release_scope": "beta/public test release without superiority, fully-reviewed, or world-class claims", + "deferred_evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "deferred_human_review": true + }, + "actual": { + "beta_test_ready": false, + "public_claim_ready": false, + "human_review_complete": false, + "beta_release_ready": false, + "beta_release_scope": "beta/public test release without superiority, fully-reviewed, or world-class claims", + "deferred_evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "deferred_human_review": true + }, + "paths": [ + "reports/benchmark_reproducibility.json", + "reports/world_class_evidence_ledger.json" + ], + "detail": "Beta/public testing may proceed with human blind review deferred, but public world-class or superiority claims must remain blocked." + }, + { + "key": "preflight-world-class-boundary", + "label": "Preflight mirrors ledger without accepting evidence", + "status": "pass", + "expected": { + "pending_count": 4, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "preflight_counts_as_evidence": false, + "credential_value_exposed": false + }, + "actual": { + "pending_count": 4, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "preflight_counts_as_evidence": false, + "credential_value_exposed": false + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/world_class_evidence_preflight.json" + ], + "detail": "Collection preflight may help operators gather evidence, but it must not print secrets or change world-class readiness." + }, + { + "key": "preflight-submission-kit-handoff", + "label": "Preflight exposes a safe submission-kit handoff", + "status": "pass", + "expected": { + "directory": "evidence/world_class/submissions", + "drafts_count_as_evidence": false, + "preflight_counts_submission_as_completion": false, + "html_report": "reports/world_class_evidence_preflight.html", + "html_exists": true, + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard .", + "artifact_prefill_counts_as_evidence": false, + "artifact_role_source": "world-class-submission-kit", + "artifact_role_counts_as_evidence": false, + "artifact_role_prefill_counts_as_evidence": false, + "submission_ref_role_present": true, + "supporting_evidence_role_present": true, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false, + "submission_ref_total_present": true, + "supporting_evidence_total_present": true + }, + "actual": { + "directory": "evidence/world_class/submissions", + "drafts_count_as_evidence": false, + "preflight_counts_submission_as_completion": false, + "html_report": "reports/world_class_evidence_preflight.html", + "html_exists": true, + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard .", + "artifact_prefill_counts_as_evidence": false, + "artifact_role_source": "world-class-submission-kit", + "artifact_role_counts_as_evidence": false, + "artifact_role_prefill_counts_as_evidence": false, + "submission_ref_role_present": true, + "supporting_evidence_role_present": true, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false, + "submission_ref_total_present": true, + "supporting_evidence_total_present": true + }, + "paths": [ + "reports/world_class_evidence_preflight.json", + "reports/world_class_evidence_preflight.html" + ], + "detail": "Preflight must give operators the exact draft, SHA-prefill, intake, review, ledger, and claim-guard commands without letting drafts, prefill, or submissions count as accepted evidence." + }, + { + "key": "review-studio-preflight-artifact-role-handoff", + "label": "Review Studio mirrors preflight artifact roles", + "status": "pass", + "expected": { + "provider-holdout": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 1, + "submission_ref_ready_count": 1, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + }, + "human-adjudication": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 8, + "supporting_evidence_ready_count": 8, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + }, + "native-permission-enforcement": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 11, + "supporting_evidence_ready_count": 11, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + }, + "native-client-telemetry": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + } + }, + "actual": { + "provider-holdout": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 1, + "submission_ref_ready_count": 1, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + }, + "human-adjudication": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 8, + "supporting_evidence_ready_count": 8, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + }, + "native-permission-enforcement": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 11, + "supporting_evidence_ready_count": 11, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + }, + "native-client-telemetry": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "submission_ref_copy_to_artifact_refs": true, + "supporting_evidence_copy_to_artifact_refs": false + } + }, + "paths": [ + "reports/world_class_evidence_preflight.json", + "reports/review-studio.json" + ], + "detail": "The Review Studio world-class action card must carry the same submission-ref versus supporting-evidence contract as the preflight handoff." + }, + { + "key": "world-class-phase-queue-consistency", + "label": "World-class phase queues mirror repair rows", + "status": "pass", + "expected": { + "summary": { + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false + }, + "top_level_phase_queue": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "item_phase_queues": { + "provider-holdout": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "human-adjudication": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-permission-enforcement": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-client-telemetry": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ] + }, + "phase_queue_counts_as_completion": false, + "operator_runbook_summary": { + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false + }, + "operator_runbook_top_level_phase_queue": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "operator_runbook_phase_queues": { + "provider-holdout": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "human-adjudication": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-permission-enforcement": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-client-telemetry": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ] + }, + "review_studio_phase_queues": { + "provider-holdout": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "human-adjudication": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-permission-enforcement": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-client-telemetry": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ] + } + }, + "actual": { + "summary": { + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false + }, + "top_level_phase_queue": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "operator_runbook_summary": { + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false + }, + "operator_runbook_top_level_phase_queue": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "item_phase_queues": { + "provider-holdout": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "human-adjudication": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-permission-enforcement": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-client-telemetry": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ] + }, + "operator_runbook_phase_queues": { + "provider-holdout": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "human-adjudication": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-permission-enforcement": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-client-telemetry": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ] + }, + "review_studio_phase_queues": { + "provider-holdout": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "human-adjudication": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-permission-enforcement": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "native-client-telemetry": [ + { + "phase": "unblock-access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ] + }, + "phase_queue_counts_as_completion": false + }, + "paths": [ + "reports/world_class_evidence_preflight.json", + "reports/world_class_operator_runbook.json", + "reports/review-studio.json" + ], + "detail": "Phase queues must be derived from repair rows in preflight and mirrored into the operator runbook and Review Studio without counting queue guidance as completion evidence." + }, + { + "key": "review-studio-no-overclaim", + "label": "Review Studio does not overclaim pending world-class evidence", + "status": "pass", + "expected": false, + "actual": false, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/review-studio.json" + ], + "detail": "When world-class evidence is pending, Review Studio must stay in a review or warning posture." + }, + { + "key": "claim-guard-package-runtime-surface", + "label": "Claim guard covers package and runtime claim surfaces", + "status": "pass", + "expected": { + "overclaim_guard_active": true, + "violation_count": 0, + "ledger_ready_to_claim_world_class": false, + "ledger_pending_count": 4, + "metadata_covers_json": true, + "package_surface_minimum": true, + "claim_surface_covers_package": true, + "required_surfaces": { + "README.md": true, + "SKILL.md": true, + "manifest.json": true, + "agents/interface.yaml": true, + "evidence/world_class/README.md": true, + "security/permission_policy.json": true, + "reports/world_class_evidence_ledger.json": true, + "dist/manifest.json": true, + "dist/targets/openai/adapter.json": true + }, + "prohibited_surfaces": [] + }, + "actual": { + "overclaim_guard_active": true, + "violation_count": 0, + "ledger_ready_to_claim_world_class": false, + "ledger_pending_count": 4, + "metadata_covers_json": true, + "package_surface_minimum": true, + "claim_surface_covers_package": true, + "required_surfaces": { + "README.md": true, + "SKILL.md": true, + "manifest.json": true, + "agents/interface.yaml": true, + "evidence/world_class/README.md": true, + "security/permission_policy.json": true, + "reports/world_class_evidence_ledger.json": true, + "dist/manifest.json": true, + "dist/targets/openai/adapter.json": true + }, + "prohibited_surfaces": [] + }, + "paths": [ + "reports/world_class_claim_guard.json", + "manifest.json", + "agents/interface.yaml", + "dist/manifest.json", + "dist/targets/openai/adapter.json", + "evidence/world_class/README.md", + "security/permission_policy.json", + "reports/world_class_evidence_ledger.json" + ], + "detail": "The overclaim guard must scan package manifests, adapter metadata, security policy, and ledger surfaces before public readiness can be trusted." + }, + { + "key": "world-class-evidence-workflow-coverage", + "label": "World-class evidence workflows cover every pending ledger entry", + "status": "pass", + "expected": { + "keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "plan_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "intake_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "submission_review_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "operator_runbook_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "operator_coordination_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "operator_coordination_counts_as_completion": false, + "operator_release_gate_ready": false, + "operator_release_gate_counts_as_completion": false, + "review_studio_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "intake_ready_to_claim_world_class": false, + "submission_review_ready_to_claim_world_class": false, + "submission_review_counts_as_completion": false, + "operator_runbook_ready_to_claim_world_class": false, + "operator_runbook_counts_as_completion": false, + "next_actions_present": { + "human-adjudication": true, + "native-client-telemetry": true, + "native-permission-enforcement": true, + "provider-holdout": true + }, + "commands": { + "human-adjudication": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + }, + "native-client-telemetry": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + }, + "native-permission-enforcement": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + }, + "provider-holdout": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + } + } + }, + "actual": { + "keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "plan_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "intake_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "submission_review_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "operator_runbook_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "operator_coordination_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "operator_coordination_counts_as_completion": false, + "operator_release_gate_ready": false, + "operator_release_gate_counts_as_completion": false, + "review_studio_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "intake_ready_to_claim_world_class": false, + "submission_review_ready_to_claim_world_class": false, + "submission_review_counts_as_completion": false, + "operator_runbook_ready_to_claim_world_class": false, + "operator_runbook_counts_as_completion": false, + "next_actions_present": { + "human-adjudication": true, + "native-client-telemetry": true, + "native-permission-enforcement": true, + "provider-holdout": true + }, + "commands": { + "human-adjudication": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + }, + "native-client-telemetry": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + }, + "native-permission-enforcement": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + }, + "provider-holdout": { + "intake": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "operator_runbook": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + }, + "review_studio": { + "prepare_submission": true, + "validate_intake": true, + "submission_review": true, + "refresh_ledger": true, + "guard_claim": true + } + } + } + }, + "paths": [ + "reports/world_class_evidence_ledger.json", + "reports/world_class_evidence_plan.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_submission_review.json", + "reports/world_class_operator_runbook.json", + "reports/review-studio.json" + ], + "detail": "Every pending world-class evidence key must have matching plan, intake, submission review, operator runbook, and Review Studio actions without counting planned work as completion." + }, + { + "key": "skill-os-2-review-current-evidence", + "label": "Skill OS 2.0 review summary mirrors current evidence", + "status": "pass", + "expected": [ + "score `91`", + "`16` gates", + "`3` warnings", + "`69` declared internal modules", + "`86 / 86` CLI help smoke checks passing across `155` scripts", + "`580` zip entries", + "archive with `580` entries", + "`12` installer permission checks enforced", + "`0` permission failures", + "`25` required artifacts", + "`23` reproduction commands", + "initial load `987/1000`", + "target count is `83`" + ], + "actual": "all present", + "paths": [ + "reports/skill-os-2-review.md", + "reports/review-studio.json", + "reports/package_verification.json", + "reports/install_simulation.json", + "reports/security_trust_report.json", + "reports/context_budget.json", + "reports/benchmark_reproducibility.json", + "scripts/ci_test.py" + ], + "detail": "Manual 2.0 review summaries must not drift from generated gate, package, trust, context, benchmark, or CI evidence." + } + ], + "artifacts": { + "json": "reports/evidence_consistency.json", + "markdown": "reports/evidence_consistency.md" + } +} diff --git a/reports/evidence_consistency.md b/reports/evidence_consistency.md new file mode 100644 index 0000000..995a851 --- /dev/null +++ b/reports/evidence_consistency.md @@ -0,0 +1,57 @@ +# Evidence Consistency + +Generated at: `2026-07-02` + +## Summary + +- decision: `consistent` +- checks: `39` +- pass: `39` +- warn: `0` +- fail: `0` + +This gate compares generated evidence reports against each other. It does not create provider, human, native-client, or permission-enforcement evidence; it only catches drift between reports that already exist. + +## Checks + +| Check | Status | Detail | Paths | +| --- | --- | --- | --- | +| Required report artifacts are readable | `pass` | The consistency gate can only be trusted when every source JSON report parses and every source Markdown report is readable. | `reports/benchmark_reproducibility.json`, `reports/skill-overview.json`, `reports/skill-interpretation.json`, `reports/adoption_drift_report.json`, `reports/world_class_evidence_ledger.json`, `reports/world_class_evidence_plan.json`, `reports/world_class_evidence_intake.json`, `reports/world_class_evidence_preflight.json`, `reports/world_class_submission_review.json`, `reports/world_class_operator_runbook.json`, `reports/skill_os2_coverage.json`, `reports/review-studio.json`, `reports/package_verification.json`, `reports/install_simulation.json`, `reports/security_trust_report.json`, `reports/context_budget.json`, `reports/world_class_claim_guard.json`, `reports/skill-os-2-review.md` | +| Release evidence flow covers first-class reports | `pass` | Release refresh and clean-lock instructions must regenerate every first-class report before evidence consistency can be trusted. | `AGENTS.md`, `reports/output_execution_runs.json`, `reports/install_simulation.json`, `reports/security_trust_report.json`, `reports/registry_audit.json`, `reports/package_verification.json`, `reports/upgrade_check.json`, `reports/adoption_drift_report.json`, `reports/architecture_maintainability.json`, `reports/python_compatibility.json`, `reports/runtime_permission_probes.json`, `reports/review_waivers.json`, `reports/review_annotations.json`, `reports/skill_atlas.json`, `reports/skill_os2_audit.json`, `reports/skill_os2_coverage.json`, `reports/context_budget.json`, `reports/context_budget_summary.json`, `reports/benchmark_reproducibility.json`, `reports/skill-overview.json`, `reports/skill-interpretation.json`, `reports/review-viewer.json`, `reports/world_class_evidence_preflight.json`, `reports/skillops/daily`, `reports/skillops/weekly`, `reports/review-studio.json`, `reports/evidence_consistency.json` | +| Review Studio gates mirror review actions | `pass` | Every non-pass Review Studio gate must have exactly one top-level review action and a gate-local action summary with source refs and a verification command. | `reports/review-studio.json` | +| Review Studio mirrors context budget governance | `pass` | Review Studio must not keep stale context warnings after context reports prove large deferred resources are governed. | `reports/context_budget.json`, `reports/review-studio.json` | +| Benchmark release lock matches source dirty state | `pass` | The benchmark release lock must be blocked by source changes, while generated evidence artifacts are tracked as generation context. | `reports/benchmark_reproducibility.json` | +| Clean worktree keeps a clean benchmark release lock | `pass` | Dirty or non-git worktrees cannot prove final release-lock freshness, so this check is advisory until the final clean-lock pass. | `reports/benchmark_reproducibility.json` | +| Human-facing reports expose the canonical Skill IR artifact | `pass` | Skill IR is the 2.0 platform-neutral semantic source, so user-facing reports must link to the artifact that actually exists. | `reports/skill-overview.json`, `reports/skill-interpretation.json`, `reports/review-studio.json`, `skill-ir/examples/yao-meta-skill.json` | +| overview embeds the benchmark commit | `pass` | Human-facing reports must point to the same benchmark release-lock commit. | `reports/benchmark_reproducibility.json`, `reports/skill-overview.json` | +| overview embeds benchmark summary fields | `pass` | Selected summary fields must match exactly across generated reports. | `reports/benchmark_reproducibility.json`, `reports/skill-overview.json` | +| overview embeds adoption drift summary fields | `pass` | Selected summary fields must match exactly across generated reports. | `reports/adoption_drift_report.json`, `reports/skill-overview.json` | +| overview embeds world-class ledger summary fields | `pass` | Selected summary fields must match exactly across generated reports. | `reports/world_class_evidence_ledger.json`, `reports/skill-overview.json` | +| overview derives readiness from the ledger | `pass` | Readiness summaries must be derived from the evidence ledger, not hand-maintained copy. | `reports/world_class_evidence_ledger.json`, `reports/skill-overview.json` | +| interpretation embeds the benchmark commit | `pass` | Human-facing reports must point to the same benchmark release-lock commit. | `reports/benchmark_reproducibility.json`, `reports/skill-interpretation.json` | +| interpretation embeds benchmark summary fields | `pass` | Selected summary fields must match exactly across generated reports. | `reports/benchmark_reproducibility.json`, `reports/skill-interpretation.json` | +| interpretation embeds adoption drift summary fields | `pass` | Selected summary fields must match exactly across generated reports. | `reports/adoption_drift_report.json`, `reports/skill-interpretation.json` | +| interpretation embeds world-class ledger summary fields | `pass` | Selected summary fields must match exactly across generated reports. | `reports/world_class_evidence_ledger.json`, `reports/skill-interpretation.json` | +| interpretation derives readiness from the ledger | `pass` | Readiness summaries must be derived from the evidence ledger, not hand-maintained copy. | `reports/world_class_evidence_ledger.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share scorecard | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share capability_profile | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share principle_model | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share contract_boundary | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share quality_review | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share risk_governance | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share world_class_readiness | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share package_assets | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| Overview and interpretation share iteration_roadmap | `pass` | The first-class interpretation report must stay in lockstep with the canonical overview model. | `reports/skill-overview.json`, `reports/skill-interpretation.json` | +| overview has a stable HTML contract | `pass` | Report output paths and language defaults are part of the user-facing contract. | `reports/skill-overview.json`, `reports/skill-overview.html` | +| interpretation has a stable HTML contract | `pass` | Report output paths and language defaults are part of the user-facing contract. | `reports/skill-interpretation.json`, `reports/skill-interpretation.html` | +| Coverage report mirrors world-class evidence boundary | `pass` | Blueprint coverage can be locally complete while public world-class evidence remains pending. | `reports/world_class_evidence_ledger.json`, `reports/skill_os2_coverage.json` | +| Benchmark report mirrors world-class evidence boundary | `pass` | Benchmark reproducibility must not overstate public claim readiness. | `reports/world_class_evidence_ledger.json`, `reports/benchmark_reproducibility.json` | +| Benchmark separates beta testing from public claims | `pass` | Beta/public testing may proceed with human blind review deferred, but public world-class or superiority claims must remain blocked. | `reports/benchmark_reproducibility.json`, `reports/world_class_evidence_ledger.json` | +| Preflight mirrors ledger without accepting evidence | `pass` | Collection preflight may help operators gather evidence, but it must not print secrets or change world-class readiness. | `reports/world_class_evidence_ledger.json`, `reports/world_class_evidence_preflight.json` | +| Preflight exposes a safe submission-kit handoff | `pass` | Preflight must give operators the exact draft, SHA-prefill, intake, review, ledger, and claim-guard commands without letting drafts, prefill, or submissions count as accepted evidence. | `reports/world_class_evidence_preflight.json`, `reports/world_class_evidence_preflight.html` | +| Review Studio mirrors preflight artifact roles | `pass` | The Review Studio world-class action card must carry the same submission-ref versus supporting-evidence contract as the preflight handoff. | `reports/world_class_evidence_preflight.json`, `reports/review-studio.json` | +| World-class phase queues mirror repair rows | `pass` | Phase queues must be derived from repair rows in preflight and mirrored into the operator runbook and Review Studio without counting queue guidance as completion evidence. | `reports/world_class_evidence_preflight.json`, `reports/world_class_operator_runbook.json`, `reports/review-studio.json` | +| Review Studio does not overclaim pending world-class evidence | `pass` | When world-class evidence is pending, Review Studio must stay in a review or warning posture. | `reports/world_class_evidence_ledger.json`, `reports/review-studio.json` | +| Claim guard covers package and runtime claim surfaces | `pass` | The overclaim guard must scan package manifests, adapter metadata, security policy, and ledger surfaces before public readiness can be trusted. | `reports/world_class_claim_guard.json`, `manifest.json`, `agents/interface.yaml`, `dist/manifest.json`, `dist/targets/openai/adapter.json`, `evidence/world_class/README.md`, `security/permission_policy.json`, `reports/world_class_evidence_ledger.json` | +| World-class evidence workflows cover every pending ledger entry | `pass` | Every pending world-class evidence key must have matching plan, intake, submission review, operator runbook, and Review Studio actions without counting planned work as completion. | `reports/world_class_evidence_ledger.json`, `reports/world_class_evidence_plan.json`, `reports/world_class_evidence_intake.json`, `reports/world_class_submission_review.json`, `reports/world_class_operator_runbook.json`, `reports/review-studio.json` | +| Skill OS 2.0 review summary mirrors current evidence | `pass` | Manual 2.0 review summaries must not drift from generated gate, package, trust, context, benchmark, or CI evidence. | `reports/skill-os-2-review.md`, `reports/review-studio.json`, `reports/package_verification.json`, `reports/install_simulation.json`, `reports/security_trust_report.json`, `reports/context_budget.json`, `reports/benchmark_reproducibility.json`, `scripts/ci_test.py` | diff --git a/reports/family_summary.md b/reports/family_summary.md new file mode 100644 index 0000000..a258b50 --- /dev/null +++ b/reports/family_summary.md @@ -0,0 +1,34 @@ +# Eval Family Summary + +- total cases: `66` +- families: `21` +- false positives: `0` +- false negatives: `0` +- average precision: `1.0` +- average recall: `1.0` + +## Family Results + +| Family | Cases | Passed | FP | FN | Pass Rate | +| --- | ---: | ---: | ---: | ---: | ---: | +| `brainstorm_only` | 2 | 2 | 0 | 0 | 1.0 | +| `brainstorm_vs_build` | 1 | 1 | 0 | 0 | 1.0 | +| `complex_multi_asset` | 3 | 3 | 0 | 0 | 1.0 | +| `document_export_vs_agent_skill` | 4 | 4 | 0 | 0 | 1.0 | +| `document_only` | 3 | 3 | 0 | 0 | 1.0 | +| `explain_not_package` | 1 | 1 | 0 | 0 | 1.0 | +| `explain_only` | 5 | 5 | 0 | 0 | 1.0 | +| `future_outline_vs_build` | 4 | 4 | 0 | 0 | 1.0 | +| `iterate_existing_skill` | 5 | 5 | 0 | 0 | 1.0 | +| `long_context_document_only` | 3 | 3 | 0 | 0 | 1.0 | +| `long_context_near_neighbor` | 3 | 3 | 0 | 0 | 1.0 | +| `long_context_summary_only` | 2 | 2 | 0 | 0 | 1.0 | +| `long_context_trigger` | 4 | 4 | 0 | 0 | 1.0 | +| `meta_skill_creation` | 1 | 1 | 0 | 0 | 1.0 | +| `one_off_vs_reusable` | 2 | 2 | 0 | 0 | 1.0 | +| `package_for_team` | 2 | 2 | 0 | 0 | 1.0 | +| `paraphrase_trigger` | 5 | 5 | 0 | 0 | 1.0 | +| `partial_scaffold_not_full_skill` | 4 | 4 | 0 | 0 | 1.0 | +| `summary_only` | 3 | 3 | 0 | 0 | 1.0 | +| `translate_only` | 4 | 4 | 0 | 0 | 1.0 | +| `workflow_to_skill` | 5 | 5 | 0 | 0 | 1.0 | diff --git a/reports/github-benchmark-scan.json b/reports/github-benchmark-scan.json new file mode 100644 index 0000000..a76b059 --- /dev/null +++ b/reports/github-benchmark-scan.json @@ -0,0 +1,188 @@ +{ + "ok": true, + "query": "skills", + "source": "github-api", + "network_boundary": "github-api-only", + "repositories": [ + { + "full_name": "obra/superpowers", + "html_url": "https://github.com/obra/superpowers", + "description": "An agentic skills framework & software development methodology that works.", + "stars": 226125, + "topics": [], + "primary_language": "Shell", + "patterns": [ + { + "category": "method", + "matched": "process", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale." + }, + { + "category": "execution", + "matched": "cli", + "borrow": "Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not copy repo-specific commands or environment assumptions verbatim." + }, + { + "category": "evaluation", + "matched": "test", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here." + }, + { + "category": "structure", + "matched": "docs", + "borrow": "Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not mirror documentation bulk that adds context cost without improving reliability." + } + ], + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure.", + "Borrow explicit validation and quality gates that make iteration safer." + ], + "avoid": [ + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim.", + "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here." + ], + "readme_excerpt": "# Superpowers\n\nSuperpowers is a complete software development methodology for your coding agents, built on top of a set of composable skills and some initial instructions that make sure your agent uses them.\n\n## Quickstart\n\nGive your agent Superpowers: [Claude Code](#claude-code), [Codex CLI](#codex-cli), [Codex App](#codex-app), [Factory Droid](#factory-droid), [Gemini CLI](#gemini-cli), [OpenCode](#opencode), [Cursor](#cursor), [GitHub Copilot CLI](#github-copilot-cli).\n\n## How it works\n\nIt starts from the moment you fire up your coding agent. As soon as it sees that you're building something, it *doesn't* just jump into trying to write code. Instead, it steps back and asks you what you're really trying to do. \n\nOnce it's teased a spec out of the conversation, it shows it to you in chunks short enough to actually read and digest. \n\nAfter you've signed off on the design, your agent puts together an implementation plan that's clear enough for an enthusiastic junior engineer with poor taste, no judgement, no project context, and an aversion to testing to follow. It emphasizes true red/green TDD, YAGNI (You Aren't Gonna Need It), and DRY. \n\nNext up, once you say \"go\", it launches a *" + }, + { + "full_name": "affaan-m/ECC", + "html_url": "https://github.com/affaan-m/ECC", + "description": "The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.", + "stars": 214381, + "topics": [ + "ai-agents", + "anthropic", + "claude", + "claude-code", + "developer-tools", + "llm", + "mcp", + "productivity" + ], + "primary_language": "JavaScript", + "patterns": [ + { + "category": "method", + "matched": "workflow", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale." + }, + { + "category": "execution", + "matched": "command, shell, script", + "borrow": "Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not copy repo-specific commands or environment assumptions verbatim." + }, + { + "category": "structure", + "matched": "guide, docs", + "borrow": "Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not mirror documentation bulk that adds context cost without improving reliability." + }, + { + "category": "governance", + "matched": "security, review", + "borrow": "Borrow the explicit review, safety, or operational trust boundaries.", + "avoid": "Do not import compliance-heavy process where the new skill does not need it." + } + ], + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure.", + "Borrow the way it separates explanation, examples, and reusable structure." + ], + "avoid": [ + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim.", + "Do not mirror documentation bulk that adds context cost without improving reliability." + ], + "readme_excerpt": "**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md) | [ไทย](docs/th/README.md) | [Deutsch](docs/de-DE/README.md) | [Español](docs/es/README.md)\n\n![ECC — the agent harness operating system](assets/hero.png)\n\n[![Stars](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.ecc.tools%2Fbadge%2Fstars&style=flat)](https://github.com/affaan-m/ECC/stargazers)\n[![Forks](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.ecc.tools%2Fbadge%2Fforks&style=flat)](https://github.com/affaan-m/ECC/network/members)\n[![Contributors](https://img.shields.io/github/contributors/affaan-m/ECC?style=flat)](https://github.com/affaan-m/ECC/graphs/contributors)\n[![npm ecc-universal](https://img.shields.io/npm/dw/ecc-universal?label=ecc-universal%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-universal)\n[![npm ecc-agentshield](https://img.shields.io/npm/dw/ecc-agentshield?label=ecc-agentshield%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-agen" + }, + { + "full_name": "multica-ai/andrej-karpathy-skills", + "html_url": "https://github.com/multica-ai/andrej-karpathy-skills", + "description": "A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathy's observations on LLM coding pitfalls.", + "stars": 174264, + "topics": [], + "primary_language": null, + "patterns": [ + { + "category": "evaluation", + "matched": "test, validation", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here." + }, + { + "category": "structure", + "matched": "guide", + "borrow": "Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not mirror documentation bulk that adds context cost without improving reliability." + }, + { + "category": "portability", + "matched": "plugin", + "borrow": "Borrow how it keeps core semantics stable across environments or integrations.", + "avoid": "Do not inherit platform lock-in or vendor-specific branching unless truly required." + } + ], + "borrow": [ + "Borrow explicit validation and quality gates that make iteration safer.", + "Borrow the way it separates explanation, examples, and reusable structure.", + "Borrow how it keeps core semantics stable across environments or integrations." + ], + "avoid": [ + "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "Do not mirror documentation bulk that adds context cost without improving reliability.", + "Do not inherit platform lock-in or vendor-specific branching unless truly required." + ], + "readme_excerpt": "# Karpathy-Inspired Claude Code Guidelines\n\n> Check out my new project [Multica](https://github.com/multica-ai/multica) — an open-source platform for running and managing coding agents with reusable skills.\n>\n> Follow me on X: [https://x.com/jiayuan_jy](https://x.com/jiayuan_jy)\n\nA single `CLAUDE.md` file to improve Claude Code behavior, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.\n\nEnglish | [简体中文](./README.zh.md)\n\n## The Problems\n\nFrom Andrej's post:\n\n> \"The models make wrong assumptions on your behalf and just run along with them without checking. They don't manage their confusion, don't seek clarifications, don't surface inconsistencies, don't present tradeoffs, don't push back when they should.\"\n\n> \"They really like to overcomplicate code and APIs, bloat abstractions, don't clean up dead code... implement a bloated construction over 1000 lines when 100 would do.\"\n\n> \"They still sometimes change/remove comments and code they don't sufficiently understand as side effects, even if orthogonal to the task.\"\n\n## The Solution\n\nFour principles in one file that directly address these issues:\n\n| Principle | Addr" + } + ], + "cross_repo": { + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure.", + "Borrow explicit validation and quality gates that make iteration safer.", + "Borrow the way it separates explanation, examples, and reusable structure.", + "Borrow how it keeps core semantics stable across environments or integrations.", + "Ask the user which of these patterns feels most worth borrowing before freezing the package shape." + ], + "avoid": [ + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim.", + "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "Do not mirror documentation bulk that adds context cost without improving reliability.", + "Do not inherit platform lock-in or vendor-specific branching unless truly required." + ] + }, + "external_references": [ + { + "name": "obra/superpowers", + "category": "method", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "source": "external" + }, + { + "name": "affaan-m/ECC", + "category": "method", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "source": "external" + }, + { + "name": "multica-ai/andrej-karpathy-skills", + "category": "evaluation", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "source": "external" + } + ], + "borrow_prompt": "I found 3 public GitHub projects worth studying. Do you want to borrow any of these patterns for method, structure, execution, or portability?", + "warnings": [] +} diff --git a/reports/github-benchmark-scan.md b/reports/github-benchmark-scan.md new file mode 100644 index 0000000..24abb4f --- /dev/null +++ b/reports/github-benchmark-scan.md @@ -0,0 +1,166 @@ +# GitHub Benchmark Scan + +- Query: `skills` +- Source: `github-api` +- Top repositories: `3` + +## Suggested Next Step + +Review the 3 benchmark objects below, then decide whether any of their patterns are worth borrowing into the new skill. + +## Top 3 Benchmark Repositories + +### obra/superpowers +- URL: https://github.com/obra/superpowers +- Stars: `226125` +- Description: An agentic skills framework & software development methodology that works. +- Topics: None + +#### Patterns worth studying + +- **method** (`process`): Borrow the way it turns a messy workflow into a repeatable operating path. +- **execution** (`cli`): Borrow the clear execution entrypoints and command structure. +- **evaluation** (`test`): Borrow explicit validation and quality gates that make iteration safer. +- **structure** (`docs`): Borrow the way it separates explanation, examples, and reusable structure. + +#### Borrow + +- Borrow the way it turns a messy workflow into a repeatable operating path. +- Borrow the clear execution entrypoints and command structure. +- Borrow explicit validation and quality gates that make iteration safer. + +#### Avoid + +- Do not import process overhead that only exists for that project's scale. +- Do not copy repo-specific commands or environment assumptions verbatim. +- Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here. + +#### README glimpse + +# Superpowers + +Superpowers is a complete software development methodology for your coding agents, built on top of a set of composable skills and some initial instructions that make sure your agent uses them. + +## Quickstart + +Give your agent Superpowers: [Claude Code](#claude-code), [Codex CLI](#codex-cli), [Codex App](#codex-app), [Factory Droid](#factory-droid), [Gemini CLI](#gemini-cli), [OpenCode](#opencode), [Cursor](#cursor), [GitHub Copilot CLI](#github-copilot-cli). + +## How it works + +It starts from the moment you fire up your coding agent. As soon as it sees that you're building something, it *doesn't* just jump into trying to write code. Instead, it steps back and asks you what you're really trying to do. + +Once it's teased a spec out of the conversation, it shows it to you in chunks short enough to actually read and digest. + +After you've signed off on the design, your agent puts together an implementation plan that's clear enough for an enthusiastic junior engineer with poor taste, no judgement, no project context, and an aversion to testing to follow. It emphasizes true red/green TDD, YAGNI (You Aren't Gonna Need It), and DRY. + +Next up, once you say "go", it launches a * + +### affaan-m/ECC +- URL: https://github.com/affaan-m/ECC +- Stars: `214381` +- Description: The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond. +- Topics: ai-agents, anthropic, claude, claude-code, developer-tools, llm, mcp, productivity + +#### Patterns worth studying + +- **method** (`workflow`): Borrow the way it turns a messy workflow into a repeatable operating path. +- **execution** (`command, shell, script`): Borrow the clear execution entrypoints and command structure. +- **structure** (`guide, docs`): Borrow the way it separates explanation, examples, and reusable structure. +- **governance** (`security, review`): Borrow the explicit review, safety, or operational trust boundaries. + +#### Borrow + +- Borrow the way it turns a messy workflow into a repeatable operating path. +- Borrow the clear execution entrypoints and command structure. +- Borrow the way it separates explanation, examples, and reusable structure. + +#### Avoid + +- Do not import process overhead that only exists for that project's scale. +- Do not copy repo-specific commands or environment assumptions verbatim. +- Do not mirror documentation bulk that adds context cost without improving reliability. + +#### README glimpse + +**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md) | [ไทย](docs/th/README.md) | [Deutsch](docs/de-DE/README.md) | [Español](docs/es/README.md) + +![ECC — the agent harness operating system](assets/hero.png) + +[![Stars](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.ecc.tools%2Fbadge%2Fstars&style=flat)](https://github.com/affaan-m/ECC/stargazers) +[![Forks](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.ecc.tools%2Fbadge%2Fforks&style=flat)](https://github.com/affaan-m/ECC/network/members) +[![Contributors](https://img.shields.io/github/contributors/affaan-m/ECC?style=flat)](https://github.com/affaan-m/ECC/graphs/contributors) +[![npm ecc-universal](https://img.shields.io/npm/dw/ecc-universal?label=ecc-universal%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-universal) +[![npm ecc-agentshield](https://img.shields.io/npm/dw/ecc-agentshield?label=ecc-agentshield%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-agen + +### multica-ai/andrej-karpathy-skills +- URL: https://github.com/multica-ai/andrej-karpathy-skills +- Stars: `174264` +- Description: A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathy's observations on LLM coding pitfalls. +- Topics: None + +#### Patterns worth studying + +- **evaluation** (`test, validation`): Borrow explicit validation and quality gates that make iteration safer. +- **structure** (`guide`): Borrow the way it separates explanation, examples, and reusable structure. +- **portability** (`plugin`): Borrow how it keeps core semantics stable across environments or integrations. + +#### Borrow + +- Borrow explicit validation and quality gates that make iteration safer. +- Borrow the way it separates explanation, examples, and reusable structure. +- Borrow how it keeps core semantics stable across environments or integrations. + +#### Avoid + +- Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here. +- Do not mirror documentation bulk that adds context cost without improving reliability. +- Do not inherit platform lock-in or vendor-specific branching unless truly required. + +#### README glimpse + +# Karpathy-Inspired Claude Code Guidelines + +> Check out my new project [Multica](https://github.com/multica-ai/multica) — an open-source platform for running and managing coding agents with reusable skills. +> +> Follow me on X: [https://x.com/jiayuan_jy](https://x.com/jiayuan_jy) + +A single `CLAUDE.md` file to improve Claude Code behavior, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls. + +English | [简体中文](./README.zh.md) + +## The Problems + +From Andrej's post: + +> "The models make wrong assumptions on your behalf and just run along with them without checking. They don't manage their confusion, don't seek clarifications, don't surface inconsistencies, don't present tradeoffs, don't push back when they should." + +> "They really like to overcomplicate code and APIs, bloat abstractions, don't clean up dead code... implement a bloated construction over 1000 lines when 100 would do." + +> "They still sometimes change/remove comments and code they don't sufficiently understand as side effects, even if orthogonal to the task." + +## The Solution + +Four principles in one file that directly address these issues: + +| Principle | Addr + +## Cross-Repo Borrow Recommendations + +- Borrow the way it turns a messy workflow into a repeatable operating path. +- Borrow the clear execution entrypoints and command structure. +- Borrow explicit validation and quality gates that make iteration safer. +- Borrow the way it separates explanation, examples, and reusable structure. +- Borrow how it keeps core semantics stable across environments or integrations. +- Ask the user which of these patterns feels most worth borrowing before freezing the package shape. + +## Cross-Repo Avoid Recommendations + +- Do not import process overhead that only exists for that project's scale. +- Do not copy repo-specific commands or environment assumptions verbatim. +- Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here. +- Do not mirror documentation bulk that adds context cost without improving reliability. +- Do not inherit platform lock-in or vendor-specific branching unless truly required. + +## Borrow Prompt + +I found 3 public GitHub projects worth studying. Do you want to borrow any of these patterns for method, structure, execution, or portability? diff --git a/reports/governance_score.json b/reports/governance_score.json new file mode 100644 index 0000000..c4908bf --- /dev/null +++ b/reports/governance_score.json @@ -0,0 +1,25 @@ +{ + "ok": true, + "failures": [], + "warnings": [], + "details": { + "skill_dir": "/Users/laoyao/AI Coding/03-Development/Skills/yao-meta-skill", + "manifest_present": true, + "frontmatter_name": "yao-meta-skill", + "manifest_name": "yao-meta-skill", + "status": "active", + "maturity_tier": "governed", + "review_cadence": "quarterly", + "governance_score": 90, + "declared_maturity_minimum": 90, + "computed_governance_band": "governed", + "computed_maturity_tier": "governed", + "score_breakdown": { + "metadata_integrity": 20, + "ownership_and_review": 20, + "boundary_and_eval": 15, + "operational_assets": 20, + "maintenance_evidence": 15 + } + } +} diff --git a/reports/install_simulation.json b/reports/install_simulation.json new file mode 100644 index 0000000..5f9c168 --- /dev/null +++ b/reports/install_simulation.json @@ -0,0 +1,286 @@ +{ + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-13", + "skill_dir": ".", + "package_dir": "tests/tmp_review_studio/dist", + "install_root": "tests/tmp_review_studio/install-root/simulate-yao-meta-skill", + "installed_skill_dir": "tests/tmp_review_studio/install-root/simulate-yao-meta-skill/yao-meta-skill", + "summary": { + "archive_present": true, + "archive_entry_count": 598, + "nested_skill_entry_count": 0, + "archive_extracted": true, + "entrypoint_loaded": true, + "manifest_loaded": true, + "interface_loaded": true, + "adapter_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "install_root_is_temp": false, + "failure_count": 0, + "warning_count": 0 + }, + "checks": [ + { + "id": "archive-present", + "status": "pass", + "detail": "Package archive exists: tests/tmp_review_studio/dist/yao-meta-skill.zip" + }, + { + "id": "archive-safe-paths", + "status": "pass", + "detail": "Archive has no absolute or parent-traversal entries" + }, + { + "id": "single-skill-entrypoint", + "status": "pass", + "detail": "Installed package exposes only the root SKILL.md entrypoint" + }, + { + "id": "single-top-level", + "status": "pass", + "detail": "Archive top-level directory is yao-meta-skill" + }, + { + "id": "entrypoint-load", + "status": "pass", + "detail": "Installed SKILL.md frontmatter is readable" + }, + { + "id": "entrypoint-name", + "status": "pass", + "detail": "Installed SKILL.md name matches package directory" + }, + { + "id": "entrypoint-description", + "status": "pass", + "detail": "Installed SKILL.md description is present" + }, + { + "id": "manifest-load", + "status": "pass", + "detail": "Installed manifest.json is readable" + }, + { + "id": "manifest-name", + "status": "pass", + "detail": "Installed manifest name matches package manifest" + }, + { + "id": "manifest-version", + "status": "pass", + "detail": "Installed manifest version matches package manifest" + }, + { + "id": "interface-load", + "status": "pass", + "detail": "Installed agents/interface.yaml is readable" + }, + { + "id": "overview-report", + "status": "pass", + "detail": "Installed overview report is present" + }, + { + "id": "review-studio-report", + "status": "pass", + "detail": "Installed Review Studio report is present" + }, + { + "id": "adapter-claude", + "status": "pass", + "detail": "claude adapter is readable after package install simulation" + }, + { + "id": "adapter-claude-name", + "status": "pass", + "detail": "claude adapter name matches package manifest" + }, + { + "id": "adapter-generic", + "status": "pass", + "detail": "generic adapter is readable after package install simulation" + }, + { + "id": "adapter-generic-name", + "status": "pass", + "detail": "generic adapter name matches package manifest" + }, + { + "id": "adapter-openai", + "status": "pass", + "detail": "openai adapter is readable after package install simulation" + }, + { + "id": "adapter-openai-name", + "status": "pass", + "detail": "openai adapter name matches package manifest" + }, + { + "id": "adapter-vscode", + "status": "pass", + "detail": "vscode adapter is readable after package install simulation" + }, + { + "id": "adapter-vscode-name", + "status": "pass", + "detail": "vscode adapter name matches package manifest" + }, + { + "id": "permission-policy-load", + "status": "pass", + "detail": "Installed permission policy is readable" + }, + { + "id": "permission-claude-contract", + "status": "pass", + "detail": "claude adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-claude-file_write-approved", + "status": "pass", + "detail": "claude capability file_write has active reviewer approval" + }, + { + "id": "permission-claude-file_write-target-enforcement", + "status": "pass", + "detail": "claude capability file_write has target enforcement note" + }, + { + "id": "permission-claude-network-approved", + "status": "pass", + "detail": "claude capability network has active reviewer approval" + }, + { + "id": "permission-claude-network-target-enforcement", + "status": "pass", + "detail": "claude capability network has target enforcement note" + }, + { + "id": "permission-claude-subprocess-approved", + "status": "pass", + "detail": "claude capability subprocess has active reviewer approval" + }, + { + "id": "permission-claude-subprocess-target-enforcement", + "status": "pass", + "detail": "claude capability subprocess has target enforcement note" + }, + { + "id": "permission-generic-contract", + "status": "pass", + "detail": "generic adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-generic-file_write-approved", + "status": "pass", + "detail": "generic capability file_write has active reviewer approval" + }, + { + "id": "permission-generic-file_write-target-enforcement", + "status": "pass", + "detail": "generic capability file_write has target enforcement note" + }, + { + "id": "permission-generic-network-approved", + "status": "pass", + "detail": "generic capability network has active reviewer approval" + }, + { + "id": "permission-generic-network-target-enforcement", + "status": "pass", + "detail": "generic capability network has target enforcement note" + }, + { + "id": "permission-generic-subprocess-approved", + "status": "pass", + "detail": "generic capability subprocess has active reviewer approval" + }, + { + "id": "permission-generic-subprocess-target-enforcement", + "status": "pass", + "detail": "generic capability subprocess has target enforcement note" + }, + { + "id": "permission-openai-contract", + "status": "pass", + "detail": "openai adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-openai-file_write-approved", + "status": "pass", + "detail": "openai capability file_write has active reviewer approval" + }, + { + "id": "permission-openai-file_write-target-enforcement", + "status": "pass", + "detail": "openai capability file_write has target enforcement note" + }, + { + "id": "permission-openai-network-approved", + "status": "pass", + "detail": "openai capability network has active reviewer approval" + }, + { + "id": "permission-openai-network-target-enforcement", + "status": "pass", + "detail": "openai capability network has target enforcement note" + }, + { + "id": "permission-openai-subprocess-approved", + "status": "pass", + "detail": "openai capability subprocess has active reviewer approval" + }, + { + "id": "permission-openai-subprocess-target-enforcement", + "status": "pass", + "detail": "openai capability subprocess has target enforcement note" + }, + { + "id": "permission-vscode-contract", + "status": "pass", + "detail": "vscode adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-vscode-file_write-approved", + "status": "pass", + "detail": "vscode capability file_write has active reviewer approval" + }, + { + "id": "permission-vscode-file_write-target-enforcement", + "status": "pass", + "detail": "vscode capability file_write has target enforcement note" + }, + { + "id": "permission-vscode-network-approved", + "status": "pass", + "detail": "vscode capability network has active reviewer approval" + }, + { + "id": "permission-vscode-network-target-enforcement", + "status": "pass", + "detail": "vscode capability network has target enforcement note" + }, + { + "id": "permission-vscode-subprocess-approved", + "status": "pass", + "detail": "vscode capability subprocess has active reviewer approval" + }, + { + "id": "permission-vscode-subprocess-target-enforcement", + "status": "pass", + "detail": "vscode capability subprocess has target enforcement note" + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "archive": "tests/tmp_review_studio/dist/yao-meta-skill.zip", + "package_manifest": "tests/tmp_review_studio/dist/manifest.json", + "json": "tests/tmp_review_studio/install_simulation.json", + "markdown": "tests/tmp_review_studio/install_simulation.md" + } +} diff --git a/reports/install_simulation.md b/reports/install_simulation.md new file mode 100644 index 0000000..fcf6ac8 --- /dev/null +++ b/reports/install_simulation.md @@ -0,0 +1,77 @@ +# Install Simulation + +- OK: `True` +- Package directory: `tests/tmp_review_studio/dist` +- Archive extracted: `True` +- Nested SKILL.md entries: `0` +- Entrypoint loaded: `True` +- Manifest loaded: `True` +- Interface loaded: `True` +- Adapters readable: `4` +- Installer permissions enforced: `12` +- Installer permission failures: `0` +- Failures: `0` +- Warnings: `0` + +## Checks + +| Check | Status | Detail | +| --- | --- | --- | +| `archive-present` | `pass` | Package archive exists: tests/tmp_review_studio/dist/yao-meta-skill.zip | +| `archive-safe-paths` | `pass` | Archive has no absolute or parent-traversal entries | +| `single-skill-entrypoint` | `pass` | Installed package exposes only the root SKILL.md entrypoint | +| `single-top-level` | `pass` | Archive top-level directory is yao-meta-skill | +| `entrypoint-load` | `pass` | Installed SKILL.md frontmatter is readable | +| `entrypoint-name` | `pass` | Installed SKILL.md name matches package directory | +| `entrypoint-description` | `pass` | Installed SKILL.md description is present | +| `manifest-load` | `pass` | Installed manifest.json is readable | +| `manifest-name` | `pass` | Installed manifest name matches package manifest | +| `manifest-version` | `pass` | Installed manifest version matches package manifest | +| `interface-load` | `pass` | Installed agents/interface.yaml is readable | +| `overview-report` | `pass` | Installed overview report is present | +| `review-studio-report` | `pass` | Installed Review Studio report is present | +| `adapter-claude` | `pass` | claude adapter is readable after package install simulation | +| `adapter-claude-name` | `pass` | claude adapter name matches package manifest | +| `adapter-generic` | `pass` | generic adapter is readable after package install simulation | +| `adapter-generic-name` | `pass` | generic adapter name matches package manifest | +| `adapter-openai` | `pass` | openai adapter is readable after package install simulation | +| `adapter-openai-name` | `pass` | openai adapter name matches package manifest | +| `adapter-vscode` | `pass` | vscode adapter is readable after package install simulation | +| `adapter-vscode-name` | `pass` | vscode adapter name matches package manifest | +| `permission-policy-load` | `pass` | Installed permission policy is readable | +| `permission-claude-contract` | `pass` | claude adapter exposes target permission contract for installer enforcement | +| `permission-claude-file_write-approved` | `pass` | claude capability file_write has active reviewer approval | +| `permission-claude-file_write-target-enforcement` | `pass` | claude capability file_write has target enforcement note | +| `permission-claude-network-approved` | `pass` | claude capability network has active reviewer approval | +| `permission-claude-network-target-enforcement` | `pass` | claude capability network has target enforcement note | +| `permission-claude-subprocess-approved` | `pass` | claude capability subprocess has active reviewer approval | +| `permission-claude-subprocess-target-enforcement` | `pass` | claude capability subprocess has target enforcement note | +| `permission-generic-contract` | `pass` | generic adapter exposes target permission contract for installer enforcement | +| `permission-generic-file_write-approved` | `pass` | generic capability file_write has active reviewer approval | +| `permission-generic-file_write-target-enforcement` | `pass` | generic capability file_write has target enforcement note | +| `permission-generic-network-approved` | `pass` | generic capability network has active reviewer approval | +| `permission-generic-network-target-enforcement` | `pass` | generic capability network has target enforcement note | +| `permission-generic-subprocess-approved` | `pass` | generic capability subprocess has active reviewer approval | +| `permission-generic-subprocess-target-enforcement` | `pass` | generic capability subprocess has target enforcement note | +| `permission-openai-contract` | `pass` | openai adapter exposes target permission contract for installer enforcement | +| `permission-openai-file_write-approved` | `pass` | openai capability file_write has active reviewer approval | +| `permission-openai-file_write-target-enforcement` | `pass` | openai capability file_write has target enforcement note | +| `permission-openai-network-approved` | `pass` | openai capability network has active reviewer approval | +| `permission-openai-network-target-enforcement` | `pass` | openai capability network has target enforcement note | +| `permission-openai-subprocess-approved` | `pass` | openai capability subprocess has active reviewer approval | +| `permission-openai-subprocess-target-enforcement` | `pass` | openai capability subprocess has target enforcement note | +| `permission-vscode-contract` | `pass` | vscode adapter exposes target permission contract for installer enforcement | +| `permission-vscode-file_write-approved` | `pass` | vscode capability file_write has active reviewer approval | +| `permission-vscode-file_write-target-enforcement` | `pass` | vscode capability file_write has target enforcement note | +| `permission-vscode-network-approved` | `pass` | vscode capability network has active reviewer approval | +| `permission-vscode-network-target-enforcement` | `pass` | vscode capability network has target enforcement note | +| `permission-vscode-subprocess-approved` | `pass` | vscode capability subprocess has active reviewer approval | +| `permission-vscode-subprocess-target-enforcement` | `pass` | vscode capability subprocess has target enforcement note | + +## Failures + +- None + +## Warnings + +- None diff --git a/reports/intent-confidence.json b/reports/intent-confidence.json new file mode 100644 index 0000000..7401f59 --- /dev/null +++ b/reports/intent-confidence.json @@ -0,0 +1,53 @@ +{ + "score": 100, + "band": "high", + "gate_passed": true, + "strengths": [ + "The recurring job is concrete enough to anchor the package.", + "Real input shape is explicit.", + "The hand-back output is concrete.", + "Boundary exclusions are already explicit.", + "Operational constraints are visible." + ], + "gaps": [], + "follow_up_questions": [], + "anchor_sentence": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse. Primary output: A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.. Exclusions: one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill, general code review or debugging unless the user is packaging that workflow as a skill, raw private material that was not intentionally supplied as skill evidence, platform-specific plugin creation when the request is not about an agent skill package.", + "recommended_action": "Intent is clear enough to package the first routeable version.", + "context": { + "job": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "real_inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "primary_output": "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "exclusions": [ + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill", + "raw private material that was not intentionally supplied as skill evidence", + "platform-specific plugin creation when the request is not about an agent skill package" + ], + "constraints": [ + "keep SKILL.md lean and route primarily through frontmatter description", + "put durable guidance in references, executable logic in scripts, and evidence in reports", + "default to the lightest reliable mode before adding governance weight", + "preserve portability across OpenAI, Claude, generic, and Agent Skills compatible targets", + "avoid raw prompt, output, transcript, or private content in telemetry" + ], + "standards": [ + "trigger boundaries must be tested with should-trigger and should-not-trigger cases", + "production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence", + "governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation", + "generated reports should be bilingual or reviewer-friendly when they are user-facing", + "each new asset must earn its place by reducing ambiguity, risk, or repeated work" + ], + "correction": "The 2.0 direction reframes this package from a Meta Skill factory into a Skill OS with Skill IR, compiler contracts, output eval, runtime conformance, trust, registry, Review Studio, Skill Atlas, and telemetry loops.", + "user_references": [ + "Yao Meta Skill 2.0 upgrade plan", + "Kami-style white report layout requirements for generated skill reports", + "local Waza engineering habits for review, design, diagnosis, and implementation discipline" + ] + } +} diff --git a/reports/intent-confidence.md b/reports/intent-confidence.md new file mode 100644 index 0000000..bab3942 --- /dev/null +++ b/reports/intent-confidence.md @@ -0,0 +1,26 @@ +# Intent Confidence + +- Confidence score: `100/100` +- Confidence band: `high` +- Gate passed: `True` +- Recommended action: Intent is clear enough to package the first routeable version. + +## Current Reading + +Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse. Primary output: A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.. Exclusions: one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill, general code review or debugging unless the user is packaging that workflow as a skill, raw private material that was not intentionally supplied as skill evidence, platform-specific plugin creation when the request is not about an agent skill package. + +## Strong Signals + +- The recurring job is concrete enough to anchor the package. +- Real input shape is explicit. +- The hand-back output is concrete. +- Boundary exclusions are already explicit. +- Operational constraints are visible. + +## Gaps To Close + +- No major intent gaps detected. + +## Follow-Up Questions + +- No extra follow-up questions required before the first package. diff --git a/reports/intent-context.json b/reports/intent-context.json new file mode 100644 index 0000000..492b5ae --- /dev/null +++ b/reports/intent-context.json @@ -0,0 +1,37 @@ +{ + "job": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "real_inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "primary_output": "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "exclusions": [ + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill", + "raw private material that was not intentionally supplied as skill evidence", + "platform-specific plugin creation when the request is not about an agent skill package" + ], + "constraints": [ + "keep SKILL.md lean and route primarily through frontmatter description", + "put durable guidance in references, executable logic in scripts, and evidence in reports", + "default to the lightest reliable mode before adding governance weight", + "preserve portability across OpenAI, Claude, generic, and Agent Skills compatible targets", + "avoid raw prompt, output, transcript, or private content in telemetry" + ], + "standards": [ + "trigger boundaries must be tested with should-trigger and should-not-trigger cases", + "production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence", + "governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation", + "generated reports should be bilingual or reviewer-friendly when they are user-facing", + "each new asset must earn its place by reducing ambiguity, risk, or repeated work" + ], + "correction": "The 2.0 direction reframes this package from a Meta Skill factory into a Skill OS with Skill IR, compiler contracts, output eval, runtime conformance, trust, registry, Review Studio, Skill Atlas, and telemetry loops.", + "user_references": [ + "Yao Meta Skill 2.0 upgrade plan", + "Kami-style white report layout requirements for generated skill reports", + "local Waza engineering habits for review, design, diagnosis, and implementation discipline" + ] +} diff --git a/reports/intent-dialogue.json b/reports/intent-dialogue.json new file mode 100644 index 0000000..eb48348 --- /dev/null +++ b/reports/intent-dialogue.json @@ -0,0 +1,79 @@ +{ + "skill_name": "yao-meta-skill", + "title": "Yao Meta Skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "focus": "portability-and-contract", + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", + "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster." + ], + "opening_styles": [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。" + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。" + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。" + } + ], + "optional_scaffold": [ + "The repeated job it should reliably handle", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse" + ], + "questions": [ + { + "question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?", + "why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape." + }, + { + "question": "When someone reaches for this skill in the real world, what materials will they actually hand to it?", + "why": "Input shape decides whether references, scripts, or templates are needed." + }, + { + "question": "What finished output should it hand back so the user can immediately keep moving?", + "why": "Outputs should drive the package structure before extra guidance is added." + }, + { + "question": "Which nearby requests should this skill politely refuse so the boundary stays clean?", + "why": "The exclusion list is the fastest route to better trigger quality." + }, + { + "question": "What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?", + "why": "Constraints decide how much structure, packaging, and review the skill actually needs." + }, + { + "question": "Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example?", + "why": "A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material." + }, + { + "question": "Which environments or clients must be able to consume this skill?", + "why": "This sets the minimum metadata and degradation strategy." + } + ], + "output": { + "capability_sentence": "Yao Meta Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", + "required_capture": [ + "recurring job", + "real inputs", + "required outputs", + "exclusions", + "constraints", + "reference preferences", + "first evaluation target" + ], + "recommended_first_gate": "portability and contract" + } +} \ No newline at end of file diff --git a/reports/intent-dialogue.md b/reports/intent-dialogue.md new file mode 100644 index 0000000..a42b776 --- /dev/null +++ b/reports/intent-dialogue.md @@ -0,0 +1,75 @@ +# Intent Dialogue + +Skill: `yao-meta-skill` + +## Opening Frame + +Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that. + +## Opening Tone Options + +### 温柔陪伴型 + +- Best when: 用户想法还散、还在试探,或者需要先被接住。 +- Example: 我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。 + +### 专业教练型 + +- Best when: 用户目标比较明确,希望被高效带着走。 +- Example: 我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。 + +### 共创伙伴型 + +- Best when: 用户已经有一些想法,希望一起打磨,不想被填表。 +- Example: 我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。 + +## Conversation Path + +1. Start with the user's own words, not package vocabulary. +2. Reflect the job, output, and non-goals back in one clean sentence. +3. Only then offer a tiny scaffold if it would help the user move faster. + +## Why Start Here + +Use this short dialogue before deep authoring. The goal is to learn the real job, output, exclusions, and constraints so the first package is small but accurate. + +## Current Anchor + +- Title: `Yao Meta Skill` +- Description: Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse. +- Focus: `portability-and-contract` +- Reference note: If you already have examples you admire, bring them in. We will learn the pattern, not copy the source. + +## Questions To Ask + +1. If this skill worked beautifully, what recurring job would it reliably handle for the user every time? + Why: This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape. +2. When someone reaches for this skill in the real world, what materials will they actually hand to it? + Why: Input shape decides whether references, scripts, or templates are needed. +3. What finished output should it hand back so the user can immediately keep moving? + Why: Outputs should drive the package structure before extra guidance is added. +4. Which nearby requests should this skill politely refuse so the boundary stays clean? + Why: The exclusion list is the fastest route to better trigger quality. +5. What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit? + Why: Constraints decide how much structure, packaging, and review the skill actually needs. +6. Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example? + Why: A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material. +7. Which environments or clients must be able to consume this skill? + Why: This sets the minimum metadata and degradation strategy. + +## Capture Before Drafting + +- Capability sentence: Yao Meta Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily. +- Recommended first gate: `portability and contract` +- Tiny optional scaffold: + - The repeated job it should reliably handle + - The real inputs people will hand to it + - The useful output it should hand back + - What it should clearly refuse +- Capture: `recurring job` +- Capture: `real inputs` +- Capture: `required outputs` +- Capture: `exclusions` +- Capture: `constraints` +- Capture: `reference preferences` +- Capture: `first evaluation target` diff --git a/reports/iteration-directions.json b/reports/iteration-directions.json new file mode 100644 index 0000000..e376d9e --- /dev/null +++ b/reports/iteration-directions.json @@ -0,0 +1,52 @@ +{ + "summary": { + "skill_name": "yao-meta-skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "maturity_tier": "governed", + "selection_rule": "Pick the three smallest next steps that increase reliability more than they increase context cost.", + "recommended_now": "Borrow one proven pattern on purpose", + "recommended_now_why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "defer_for_now": "Create an iteration evidence loop" + }, + "directions": [ + { + "priority": 2, + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design.", + "do_now": "Do this first.", + "wait_on": "Wait to add broader structure until this move clearly improves reliability." + }, + { + "priority": 5, + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + }, + { + "priority": 12, + "title": "Create an iteration evidence loop", + "why": "The package should show what changed and why after the first draft.", + "actions": [ + "Generate the HTML skill report and keep it aligned with the package.", + "Record reference scan choices and non-goals.", + "Capture the next iteration choice explicitly before adding more files." + ], + "unlocks": "A clearer path for the next author or reviewer.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + } + ] +} \ No newline at end of file diff --git a/reports/iteration-directions.md b/reports/iteration-directions.md new file mode 100644 index 0000000..258b620 --- /dev/null +++ b/reports/iteration-directions.md @@ -0,0 +1,62 @@ +# Iteration Directions + +Skill: `yao-meta-skill` + +- Maturity tier: `governed` +- Selection rule: Pick the three smallest next steps that increase reliability more than they increase context cost. +- Start here: `Borrow one proven pattern on purpose` +- Why first: You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely. +- Defer for now: `Create an iteration evidence loop` + +## Top 3 Next Moves + +### 2. Borrow one proven pattern on purpose + +- Why now: You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely. +- Timing: Do this first. +- Recommended actions: + - Read the strongest pattern from obra/superpowers. +- Unlocks: A cleaner package shape with less accidental over-design. +- Wait on: Wait to add broader structure until this move clearly improves reliability. + + - Decide whether to borrow method, structure, execution, or portability, but only one of them first. +- Unlocks: A cleaner package shape with less accidental over-design. +- Wait on: Wait to add broader structure until this move clearly improves reliability. + + - Record what you will not borrow so the package stays light. +- Unlocks: A cleaner package shape with less accidental over-design. +- Wait on: Wait to add broader structure until this move clearly improves reliability. + +### 5. Harden portability semantics + +- Why now: The skill already signals reuse across environments, so contract clarity matters early. +- Timing: Do this after the first move lands cleanly. +- Recommended actions: + - Confirm activation mode, execution context, and trust assumptions. +- Unlocks: Safer cross-environment reuse with less target drift. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Add or review degradation strategy for non-native targets. +- Unlocks: Safer cross-environment reuse with less target drift. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Package the skill once to verify adapter expectations. +- Unlocks: Safer cross-environment reuse with less target drift. +- Wait on: Wait until the package has evidence that this extra structure is justified. + +### 12. Create an iteration evidence loop + +- Why now: The package should show what changed and why after the first draft. +- Timing: Do this after the first move lands cleanly. +- Recommended actions: + - Generate the HTML skill report and keep it aligned with the package. +- Unlocks: A clearer path for the next author or reviewer. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Record reference scan choices and non-goals. +- Unlocks: A clearer path for the next author or reviewer. +- Wait on: Wait until the package has evidence that this extra structure is justified. + + - Capture the next iteration choice explicitly before adding more files. +- Unlocks: A clearer path for the next author or reviewer. +- Wait on: Wait until the package has evidence that this extra structure is justified. diff --git a/reports/iteration_bundles/governed-incident-command/bundle.json b/reports/iteration_bundles/governed-incident-command/bundle.json new file mode 100644 index 0000000..98501ad --- /dev/null +++ b/reports/iteration_bundles/governed-incident-command/bundle.json @@ -0,0 +1,123 @@ +{ + "target": "governed-incident-command", + "decision": "keep_current", + "winner_label": "Current", + "winner_changed": false, + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "cause_tags": [ + "no_candidate_outperformed_current", + "current_holdout_gap_present", + "current_holdout_risk", + "adversarial_watch_risk" + ], + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "candidate_registry": [ + { + "name": "governed-incident-command", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, and stakeholder requirements. Use when asked to standardize incident review, severity assessment, incident communication, or incident packet assembly into one reusable operational skill. Do not use for generic debugging, one-off incident explanations, or brainstorming possible response ideas.", + "tokens": 93, + "dev_errors": 0, + "holdout_errors": 1 + }, + { + "name": "governed-incident-command", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Trigger when requests mention alerts, incident notes, transcripts, or stakeholder requirements and the job is to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 51, + "dev_errors": 1, + "holdout_errors": 1, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "review_template": { + "target": "governed-incident-command", + "current_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "candidate_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "focus": [ + "no_candidate_outperformed_current", + "current_holdout_gap_present", + "current_holdout_risk" + ] + }, + "artifacts": { + "skill": "examples/governed-incident-command/generated-skill/SKILL.md", + "optimization_report": "examples/governed-incident-command/optimization/reports/description_optimization.json", + "promotion_decisions": "reports/promotion_decisions.json", + "candidate_registry": "reports/candidate_registry.json", + "regression_cause_taxonomy": "references/regression-cause-taxonomy.md", + "human_review_template": "references/human-review-template.md" + } +} diff --git a/reports/iteration_bundles/governed-incident-command/bundle.md b/reports/iteration_bundles/governed-incident-command/bundle.md new file mode 100644 index 0000000..4ea7fd3 --- /dev/null +++ b/reports/iteration_bundles/governed-incident-command/bundle.md @@ -0,0 +1,53 @@ +# Iteration Bundle: governed-incident-command + +- decision: `keep_current` +- winner label: `Current` +- winner changed: `False` +- next action: Keep the current description and open a new candidate only when fresh route evidence appears. + +## Cause Tags + +- `no_candidate_outperformed_current` +- `current_holdout_gap_present` +- `current_holdout_risk` +- `adversarial_watch_risk` + +## Gate Status + +| Gate | Pass | +| --- | --- | +| `visible_holdout_non_regression` | True | +| `blind_holdout_non_regression` | True | +| `judge_blind_non_regression` | True | +| `judge_blind_agreement` | True | +| `adversarial_non_regression` | True | +| `adversarial_risk_ok` | True | +| `route_confusion_clean` | True | +| `family_stability` | True | + +## Candidate Registry + +| Role | Label | Ranking State | Promotion State | Tokens | Dev Errors | Holdout Errors | +| --- | --- | --- | --- | ---: | ---: | ---: | +| baseline | `Baseline` | reference | reference | 93 | 0 | 1 | +| current | `Current` | selected_by_dev | kept_current | 37 | 0 | 1 | +| candidate | `Balanced` | not_selected | blocked | 54 | 0 | 1 | +| candidate | `Boundary` | not_selected | blocked | 78 | 0 | 1 | +| candidate | `Artifact Aware` | not_selected | blocked | 78 | 0 | 1 | +| candidate | `Guardrail` | not_selected | blocked | 51 | 1 | 1 | + +## Human Review Stub + +- target: governed-incident-command +- current description: Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. +- candidate description: Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. +- review focus: no_candidate_outperformed_current, current_holdout_gap_present, current_holdout_risk + +## Artifact Paths + +- skill: `examples/governed-incident-command/generated-skill/SKILL.md` +- optimization_report: `examples/governed-incident-command/optimization/reports/description_optimization.json` +- promotion_decisions: `reports/promotion_decisions.json` +- candidate_registry: `reports/candidate_registry.json` +- regression_cause_taxonomy: `references/regression-cause-taxonomy.md` +- human_review_template: `references/human-review-template.md` diff --git a/reports/iteration_bundles/governed-incident-command/review.md b/reports/iteration_bundles/governed-incident-command/review.md new file mode 100644 index 0000000..fe570ef --- /dev/null +++ b/reports/iteration_bundles/governed-incident-command/review.md @@ -0,0 +1,8 @@ +# Human Review + +- target: governed-incident-command +- current description: Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. +- candidate description: Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. +- suggested focus: no_candidate_outperformed_current, current_holdout_gap_present, current_holdout_risk + +Use the shared template in `references/human-review-template.md` to complete the final decision. diff --git a/reports/iteration_bundles/team-frontend-review/bundle.json b/reports/iteration_bundles/team-frontend-review/bundle.json new file mode 100644 index 0000000..bb14276 --- /dev/null +++ b/reports/iteration_bundles/team-frontend-review/bundle.json @@ -0,0 +1,119 @@ +{ + "target": "team-frontend-review", + "decision": "keep_current", + "winner_label": "Current", + "winner_changed": false, + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "cause_tags": [ + "no_candidate_outperformed_current", + "current_holdout_risk" + ], + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "candidate_registry": [ + { + "name": "team-frontend-review", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "tokens": 52, + "dev_errors": 3, + "holdout_errors": 0 + }, + { + "name": "team-frontend-review", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 62, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 64, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Trigger when requests mention React components, forms, frontend diffs, or UI code and the job is to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 84, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 90, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + } + ], + "review_template": { + "target": "team-frontend-review", + "current_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "candidate_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "focus": [ + "no_candidate_outperformed_current", + "current_holdout_risk" + ] + }, + "artifacts": { + "skill": "examples/team-frontend-review/generated-skill/SKILL.md", + "optimization_report": "examples/team-frontend-review/optimization/reports/description_optimization.json", + "promotion_decisions": "reports/promotion_decisions.json", + "candidate_registry": "reports/candidate_registry.json", + "regression_cause_taxonomy": "references/regression-cause-taxonomy.md", + "human_review_template": "references/human-review-template.md" + } +} diff --git a/reports/iteration_bundles/team-frontend-review/bundle.md b/reports/iteration_bundles/team-frontend-review/bundle.md new file mode 100644 index 0000000..c02ef7b --- /dev/null +++ b/reports/iteration_bundles/team-frontend-review/bundle.md @@ -0,0 +1,51 @@ +# Iteration Bundle: team-frontend-review + +- decision: `keep_current` +- winner label: `Current` +- winner changed: `False` +- next action: Keep the current description and open a new candidate only when fresh route evidence appears. + +## Cause Tags + +- `no_candidate_outperformed_current` +- `current_holdout_risk` + +## Gate Status + +| Gate | Pass | +| --- | --- | +| `visible_holdout_non_regression` | True | +| `blind_holdout_non_regression` | True | +| `judge_blind_non_regression` | True | +| `judge_blind_agreement` | True | +| `adversarial_non_regression` | True | +| `adversarial_risk_ok` | True | +| `route_confusion_clean` | True | +| `family_stability` | True | + +## Candidate Registry + +| Role | Label | Ranking State | Promotion State | Tokens | Dev Errors | Holdout Errors | +| --- | --- | --- | --- | ---: | ---: | ---: | +| baseline | `Baseline` | reference | reference | 52 | 3 | 0 | +| current | `Current` | selected_by_dev | kept_current | 50 | 3 | 0 | +| candidate | `Guardrail` | not_selected | blocked | 62 | 3 | 0 | +| candidate | `Balanced` | not_selected | blocked | 64 | 3 | 0 | +| candidate | `Artifact Aware` | not_selected | blocked | 84 | 3 | 0 | +| candidate | `Boundary` | not_selected | blocked | 90 | 3 | 0 | + +## Human Review Stub + +- target: team-frontend-review +- current description: Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. +- candidate description: Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. +- review focus: no_candidate_outperformed_current, current_holdout_risk + +## Artifact Paths + +- skill: `examples/team-frontend-review/generated-skill/SKILL.md` +- optimization_report: `examples/team-frontend-review/optimization/reports/description_optimization.json` +- promotion_decisions: `reports/promotion_decisions.json` +- candidate_registry: `reports/candidate_registry.json` +- regression_cause_taxonomy: `references/regression-cause-taxonomy.md` +- human_review_template: `references/human-review-template.md` diff --git a/reports/iteration_bundles/team-frontend-review/review.md b/reports/iteration_bundles/team-frontend-review/review.md new file mode 100644 index 0000000..5c0e469 --- /dev/null +++ b/reports/iteration_bundles/team-frontend-review/review.md @@ -0,0 +1,8 @@ +# Human Review + +- target: team-frontend-review +- current description: Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. +- candidate description: Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. +- suggested focus: no_candidate_outperformed_current, current_holdout_risk + +Use the shared template in `references/human-review-template.md` to complete the final decision. diff --git a/reports/iteration_bundles/yao-meta-skill/bundle.json b/reports/iteration_bundles/yao-meta-skill/bundle.json new file mode 100644 index 0000000..04c26ca --- /dev/null +++ b/reports/iteration_bundles/yao-meta-skill/bundle.json @@ -0,0 +1,135 @@ +{ + "target": "yao-meta-skill", + "decision": "keep_current", + "winner_label": "Current", + "winner_changed": false, + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "cause_tags": [ + "no_candidate_outperformed_current" + ], + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "candidate_registry": [ + { + "name": "yao-meta-skill", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Create and improve agent skills.", + "tokens": 8, + "dev_errors": 1, + "holdout_errors": 0 + }, + { + "name": "yao-meta-skill", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Minimal", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 41, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 56, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 60, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Trigger when requests mention workflows, prompts, transcripts, or docs and the job is to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 77, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 83, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "review_template": { + "target": "yao-meta-skill", + "current_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "candidate_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "focus": [ + "no_candidate_outperformed_current" + ] + }, + "artifacts": { + "skill": "SKILL.md", + "optimization_report": "reports/description_optimization.json", + "promotion_decisions": "reports/promotion_decisions.json", + "candidate_registry": "reports/candidate_registry.json", + "regression_cause_taxonomy": "references/regression-cause-taxonomy.md", + "human_review_template": "references/human-review-template.md" + } +} diff --git a/reports/iteration_bundles/yao-meta-skill/bundle.md b/reports/iteration_bundles/yao-meta-skill/bundle.md new file mode 100644 index 0000000..417c21f --- /dev/null +++ b/reports/iteration_bundles/yao-meta-skill/bundle.md @@ -0,0 +1,51 @@ +# Iteration Bundle: yao-meta-skill + +- decision: `keep_current` +- winner label: `Current` +- winner changed: `False` +- next action: Keep the current description and open a new candidate only when fresh route evidence appears. + +## Cause Tags + +- `no_candidate_outperformed_current` + +## Gate Status + +| Gate | Pass | +| --- | --- | +| `visible_holdout_non_regression` | True | +| `blind_holdout_non_regression` | True | +| `judge_blind_non_regression` | True | +| `judge_blind_agreement` | True | +| `adversarial_non_regression` | True | +| `adversarial_risk_ok` | True | +| `route_confusion_clean` | True | +| `family_stability` | True | + +## Candidate Registry + +| Role | Label | Ranking State | Promotion State | Tokens | Dev Errors | Holdout Errors | +| --- | --- | --- | --- | ---: | ---: | ---: | +| baseline | `Baseline` | reference | reference | 8 | 1 | 0 | +| current | `Current` | selected_by_dev | kept_current | 54 | 0 | 0 | +| candidate | `Minimal` | not_selected | blocked | 41 | 2 | 0 | +| candidate | `Guardrail` | not_selected | blocked | 56 | 2 | 0 | +| candidate | `Balanced` | not_selected | blocked | 60 | 2 | 0 | +| candidate | `Artifact Aware` | not_selected | blocked | 77 | 2 | 0 | +| candidate | `Boundary` | not_selected | blocked | 83 | 2 | 0 | + +## Human Review Stub + +- target: yao-meta-skill +- current description: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. +- candidate description: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. +- review focus: no_candidate_outperformed_current + +## Artifact Paths + +- skill: `SKILL.md` +- optimization_report: `reports/description_optimization.json` +- promotion_decisions: `reports/promotion_decisions.json` +- candidate_registry: `reports/candidate_registry.json` +- regression_cause_taxonomy: `references/regression-cause-taxonomy.md` +- human_review_template: `references/human-review-template.md` diff --git a/reports/iteration_bundles/yao-meta-skill/review.md b/reports/iteration_bundles/yao-meta-skill/review.md new file mode 100644 index 0000000..636577a --- /dev/null +++ b/reports/iteration_bundles/yao-meta-skill/review.md @@ -0,0 +1,8 @@ +# Human Review + +- target: yao-meta-skill +- current description: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. +- candidate description: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. +- suggested focus: no_candidate_outperformed_current + +Use the shared template in `references/human-review-template.md` to complete the final decision. diff --git a/reports/iteration_ledger.md b/reports/iteration_ledger.md new file mode 100644 index 0000000..c41782a --- /dev/null +++ b/reports/iteration_ledger.md @@ -0,0 +1,49 @@ +# Iteration Ledger + +## System Milestones + +| Date | Label | Trigger Cases | FP | FN | Route Accuracy | Notes | +| --- | --- | ---: | ---: | ---: | ---: | --- | +| 2026-03-31 | Context First Pack | 66 | 0 | 0 | - | added tiered context budgets; added unused resource detection and quality density; added root and example context budget reports | +| 2026-03-31 | Family Suite Expansion | 66 | 0 | 0 | - | expanded the family-based trigger suite; published the homepage eval dashboard | +| 2026-03-31 | Governance Quality Gates | 66 | 0 | 0 | - | added runnable governance checks; added resource-boundary checks; wired both into make test | +| 2026-03-31 | Governed Assets And History | 66 | 0 | 0 | - | added governance maturity scoring; added regression history snapshots and renderer; added governed incident-command example | +| 2026-04-01 | Route Scorecard Foundation | 13 | 0 | 0 | 1.0 | added route confusion matrix for sibling skills and no-route cases; published route scorecard and iteration ledger foundation | + +## Description Optimization Milestones + +| Date | Label | Target | Blind Errors | Judge Blind Errors | Judge Agreement | Adversarial Errors | Adversarial Gap | Adversarial Risk | Drift Note | +| --- | --- | --- | ---: | ---: | ---: | ---: | ---: | --- | --- | +| 2026-03-31 | Description Optimization Suite | `yao-meta-skill` | - | - | - | - | - | - | initial description optimization suite without blind holdout | +| 2026-03-31 | Description Optimization Suite | `team-frontend-review` | - | - | - | - | - | - | compressed example description and stored first holdout report | +| 2026-03-31 | Description Optimization Suite | `governed-incident-command` | - | - | - | - | - | - | compressed governed description and reduced visible holdout misses | +| 2026-04-01 | Adversarial Calibration And Family Drift | `yao-meta-skill` | 0 | - | - | 0 | 0.834 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.834; risk n/a -> healthy | +| 2026-04-01 | Adversarial Calibration And Family Drift | `team-frontend-review` | 0 | - | - | 0 | 0.509 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.509; risk n/a -> healthy | +| 2026-04-01 | Adversarial Calibration And Family Drift | `governed-incident-command` | 0 | - | - | 0 | 0.598 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 1; adversarial calibration +0.598; risk n/a -> healthy | +| 2026-04-01 | Blind Holdout And Drift History | `yao-meta-skill` | 0 | - | - | - | - | - | tokens stable; blind gate added with 0 errors; holdout stable at 0 | +| 2026-04-01 | Blind Holdout And Drift History | `team-frontend-review` | 0 | - | - | - | - | - | tokens stable; blind gate added with 0 errors; holdout stable at 0 | +| 2026-04-01 | Blind Holdout And Drift History | `governed-incident-command` | 0 | - | - | - | - | - | tokens stable; blind gate added with 0 errors; holdout stable at 1 | +| 2026-04-01 | Judge-Backed Blind Eval | `yao-meta-skill` | 0 | 0 | 1.0 | 0 | 0.834 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.834; risk n/a -> healthy | +| 2026-04-01 | Judge-Backed Blind Eval | `team-frontend-review` | 0 | 0 | 1.0 | 0 | 0.509 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 0; adversarial calibration +0.509; risk n/a -> healthy | +| 2026-04-01 | Judge-Backed Blind Eval | `governed-incident-command` | 0 | 0 | 1.0 | 0 | 0.598 | healthy | tokens stable; blind stable at 0; adversarial gate added with 0 errors; holdout stable at 1; adversarial calibration +0.598; risk n/a -> healthy | + +## Current Promotion Decisions + +| Target | Decision | Winner | Causes | Next Action | +| --- | --- | --- | --- | --- | +| `yao-meta-skill` | `keep_current` | `Current` | no_candidate_outperformed_current | Keep the current description and open a new candidate only when fresh route evidence appears. | +| `team-frontend-review` | `keep_current` | `Current` | no_candidate_outperformed_current, current_holdout_risk | Keep the current description and open a new candidate only when fresh route evidence appears. | +| `governed-incident-command` | `keep_current` | `Current` | no_candidate_outperformed_current, current_holdout_gap_present, current_holdout_risk, adversarial_watch_risk | Keep the current description and open a new candidate only when fresh route evidence appears. | + +## Current Route Scorecard + +- total cases: `13` +- accuracy: `1.0` +- ambiguous cases: `0` + +| Route | Precision | Recall | Avg Margin | +| --- | ---: | ---: | ---: | +| `yao-meta-skill` | 1.0 | 1.0 | 0.644 | +| `team-frontend-review` | 1.0 | 1.0 | 0.805 | +| `governed-incident-command` | 1.0 | 1.0 | 0.709 | +| `no_route` | 1.0 | 1.0 | - | diff --git a/reports/output-risk-profile.json b/reports/output-risk-profile.json new file mode 100644 index 0000000..5f5a126 --- /dev/null +++ b/reports/output-risk-profile.json @@ -0,0 +1,143 @@ +{ + "skill_name": "yao-meta-skill", + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "top_risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap." + ], + "output_constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output." + ], + "self_repair_checks": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards.", + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note.", + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ], + "reviewer_note": "Use this report before deepening the package and again before approving example outputs." +} diff --git a/reports/output-risk-profile.md b/reports/output-risk-profile.md new file mode 100644 index 0000000..88f3bdd --- /dev/null +++ b/reports/output-risk-profile.md @@ -0,0 +1,60 @@ +# Output Risk Profile + +Skill: `yao-meta-skill` + +## Why This Exists + +Generated skills often fail in small output details: generic headings, cluttered citations, fragile screenshots, weak Markdown rendering, or missing execution assumptions. This profile predicts the most likely output mistakes before the skill is used heavily. + +## Matched Risk Families + +### Markdown readability +- Matched keywords: md, report, doc +- Score: `3` + +### Citation and footnote clutter +- Matched keywords: source, reference +- Score: `2` + +### Screenshot and visual capture +- Matched keywords: capture +- Score: `1` + +### Code and command safety +- Matched keywords: script +- Score: `1` + +### Tone and specificity +- Matched keywords: summary +- Score: `1` + +## Likely Output Mistakes + +- Tables can render as dense grids with weak hierarchy or poor mobile readability. +- Long bullets can make the output look complete while hiding the actual decision logic. +- Footnote markers or dense citation notes can interrupt the reading flow. +- Evidence can be over-attached to obvious statements and under-attached to risky claims. +- Screenshots can be captured from the wrong state, wrong viewport, or wrong crop. +- Missing screenshots can cause the skill to invent visual references instead of declaring the gap. + +## Output Constraints To Apply + +- Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets. +- Keep table cells short and move explanations below the table. +- Attach citations only to claims that need evidence, not to every sentence. +- Group source notes at the end of a section when inline markers would hurt readability. +- Never invent a screenshot; state when visual evidence is missing. +- Record the source, viewport, and crop intent for any screenshot-dependent output. + +## Self-Repair Checks + +- Preview whether each table still reads well when columns are narrow. +- Convert any table with paragraph-length cells into bullets or cards. +- Remove decorative citations that do not support a material claim. +- Move repeated source explanations into one compact source note. +- Check that every screenshot reference points to a real provided or generated asset. +- Reword any visual instruction that depends on an unseen screen state. + +## Reviewer Note + +Use this report before deepening the package and again before approving example outputs. diff --git a/reports/output_blind_answer_key.json b/reports/output_blind_answer_key.json new file mode 100644 index 0000000..9de710d --- /dev/null +++ b/reports/output_blind_answer_key.json @@ -0,0 +1,56 @@ +{ + "schema_version": "1.0", + "seed": "yao-output-eval-blind-v1", + "summary": { + "pair_count": 5, + "with_skill_expected_count": 5, + "baseline_expected_count": 0 + }, + "answers": [ + { + "case_id": "skill-package-contract", + "variant_a_role": "with_skill", + "variant_b_role": "baseline", + "expected_winner_role": "with_skill", + "expected_winner_variant": "A", + "score_winner_role": "with_skill", + "delta": 100.0 + }, + { + "case_id": "output-eval-expectation", + "variant_a_role": "with_skill", + "variant_b_role": "baseline", + "expected_winner_role": "with_skill", + "expected_winner_variant": "A", + "score_winner_role": "with_skill", + "delta": 100.0 + }, + { + "case_id": "ir-before-packaging", + "variant_a_role": "baseline", + "variant_b_role": "with_skill", + "expected_winner_role": "with_skill", + "expected_winner_variant": "B", + "score_winner_role": "with_skill", + "delta": 100.0 + }, + { + "case_id": "near-neighbor-boundary", + "variant_a_role": "baseline", + "variant_b_role": "with_skill", + "expected_winner_role": "with_skill", + "expected_winner_variant": "B", + "score_winner_role": "with_skill", + "delta": 100.0 + }, + { + "case_id": "file-backed-governed-package", + "variant_a_role": "baseline", + "variant_b_role": "with_skill", + "expected_winner_role": "with_skill", + "expected_winner_variant": "B", + "score_winner_role": "with_skill", + "delta": 100.0 + } + ] +} diff --git a/reports/output_blind_review_pack.json b/reports/output_blind_review_pack.json new file mode 100644 index 0000000..27507ba --- /dev/null +++ b/reports/output_blind_review_pack.json @@ -0,0 +1,204 @@ +{ + "schema_version": "1.0", + "seed": "yao-output-eval-blind-v1", + "summary": { + "pair_count": 5, + "answer_key_separate": true, + "with_skill_hidden_count": 5 + }, + "pairs": [ + { + "case_id": "skill-package-contract", + "prompt": "Turn this repeated workflow into a reusable team skill package.", + "input_files": [], + "metadata": {}, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0 + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0 + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0 + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "skill-package-contract:A", + "output": "Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/." + }, + "variant_b": { + "blind_id": "skill-package-contract:B", + "output": "I can write a prompt for that workflow and include a short checklist." + } + }, + { + "case_id": "output-eval-expectation", + "prompt": "Upgrade this production skill so we know whether the generated output is better than baseline.", + "input_files": [], + "metadata": {}, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0 + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0 + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0 + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "output-eval-expectation:A", + "output": "Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md." + }, + "variant_b": { + "blind_id": "output-eval-expectation:B", + "output": "Add more examples and run the trigger eval again." + } + }, + { + "case_id": "ir-before-packaging", + "prompt": "Package this skill for OpenAI, Claude, Agent Skills, and generic targets.", + "input_files": [], + "metadata": {}, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0 + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0 + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0 + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "ir-before-packaging:A", + "output": "Export folders for each platform and adjust files as needed." + }, + "variant_b": { + "blind_id": "ir-before-packaging:B", + "output": "Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract." + } + }, + { + "case_id": "near-neighbor-boundary", + "prompt": "I only need a one-off summary of these notes, not a reusable process.", + "input_files": [], + "metadata": { + "case_type": "near_neighbor", + "tier": "production" + }, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0 + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0 + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "near-neighbor-boundary:A", + "output": "Create a SKILL.md and a reusable workflow anyway so future notes can use it." + }, + "variant_b": { + "blind_id": "near-neighbor-boundary:B", + "output": "Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract." + } + }, + { + "case_id": "file-backed-governed-package", + "prompt": "Turn the attached release brief source into a governed skill package.", + "input_files": [ + "fixtures/release-brief-source.md" + ], + "metadata": { + "case_type": "boundary", + "tier": "governed" + }, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0 + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0 + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0 + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0 + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "file-backed-governed-package:A", + "output": "Draft a release announcement with the changelog, support notes, and owner name." + }, + "variant_b": { + "blind_id": "file-backed-governed-package:B", + "output": "Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them." + } + } + ] +} diff --git a/reports/output_blind_review_pack.md b/reports/output_blind_review_pack.md new file mode 100644 index 0000000..ba78e24 --- /dev/null +++ b/reports/output_blind_review_pack.md @@ -0,0 +1,97 @@ +# Output Blind A/B Review Pack + +This packet hides whether each variant came from the baseline or the skill-guided output. Use the separate answer key only after review. + +- Pairs: `5` +- Seed: `yao-output-eval-blind-v1` +- Answer key separate: `True` + +## Case: skill-package-contract + +Prompt: Turn this repeated workflow into a reusable team skill package. + +Rubric: +- `has-entrypoint` (1.0): Output names the routeable Skill entrypoint. +- `has-interface` (1.0): Output includes neutral interface metadata. +- `has-report` (1.0): Output points reviewer to generated overview report. +- `has-resource-boundary` (1.0): Output preserves entrypoint/resource separation. + +### Variant A + +Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/. + +### Variant B + +I can write a prompt for that workflow and include a short checklist. + +## Case: output-eval-expectation + +Prompt: Upgrade this production skill so we know whether the generated output is better than baseline. + +Rubric: +- `has-baseline-comparison` (1.0): Output explicitly compares with-skill and baseline outputs. +- `has-assertion-grading` (1.0): Output includes objective assertion grading. +- `has-scorecard` (1.0): Output produces a scorecard report path. +- `has-failure-taxonomy` (1.0): Output records failure taxonomy. + +### Variant A + +Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md. + +### Variant B + +Add more examples and run the trigger eval again. + +## Case: ir-before-packaging + +Prompt: Package this skill for OpenAI, Claude, Agent Skills, and generic targets. + +Rubric: +- `has-ir-first` (1.0): Output requires Skill IR before packaging. +- `has-core-ir-fields` (1.0): Output lists core IR contract fields. +- `has-targets` (1.0): Output names requested runtime targets. +- `has-semantic-preservation` (1.0): Output says targets must preserve the capability contract. + +### Variant A + +Export folders for each platform and adjust files as needed. + +### Variant B + +Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract. + +## Case: near-neighbor-boundary + +Prompt: I only need a one-off summary of these notes, not a reusable process. + +Rubric: +- `declines-skill` (1.0): Output refuses unnecessary skill creation for one-off work. +- `names-near-neighbor` (1.0): Output labels the request as near-neighbor instead of owned work. +- `requires-reuse-signal` (1.0): Output asks for repeat-use evidence before packaging. + +### Variant A + +Create a SKILL.md and a reusable workflow anyway so future notes can use it. + +### Variant B + +Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract. + +## Case: file-backed-governed-package + +Prompt: Turn the attached release brief source into a governed skill package. + +Rubric: +- `uses-file-backed-evidence` (1.0): Output names file-backed source evidence. +- `has-governance` (1.0): Output preserves governed ownership metadata. +- `has-output-contract` (1.0): Output preserves output and rollback boundaries. +- `has-trust-and-scorecard` (1.0): Output requires trust and output scorecard artifacts. +- `does-not-invent-evidence` (1.0): Output forbids invented launch evidence. + +### Variant A + +Draft a release announcement with the changelog, support notes, and owner name. + +### Variant B + +Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them. diff --git a/reports/output_execution_runs.json b/reports/output_execution_runs.json new file mode 100644 index 0000000..4ce4e7c --- /dev/null +++ b/reports/output_execution_runs.json @@ -0,0 +1,312 @@ +{ + "schema_version": "1.0", + "ok": true, + "cases": "evals/output/cases.jsonl", + "runner": { + "mode": "command", + "command": [ + "python3", + "scripts/provider_output_eval_runner.py", + "--provider", + "deepseek", + "--model", + "deepseek-v4-flash", + "--api-format", + "chat-completions", + "--thinking", + "disabled", + "--api-key-env", + "DEEPSEEK_API_KEY" + ], + "timeout_seconds": 180.0 + }, + "summary": { + "case_count": 5, + "variant_run_count": 10, + "command_executed_count": 10, + "model_executed_count": 10, + "recorded_fixture_count": 0, + "timing_observed_count": 10, + "token_observed_count": 10, + "token_estimated_count": 0, + "baseline_pass_rate": 23.67, + "with_skill_pass_rate": 43.67, + "delta": 20.0, + "regression_count": 0, + "failure_count": 0, + "gate_pass": true + }, + "runs": [ + { + "case_id": "skill-package-contract", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 5980.01, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 100, + "output_tokens": 384, + "total_tokens": 484, + "estimated": false + }, + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "failed_assertions": [ + "has-entrypoint", + "has-interface", + "has-report", + "has-resource-boundary" + ], + "output_sha256": "1aa12d65d55863581eef67aaa173b3a9962524a91533693044cb340a342b760b", + "failure": "" + }, + { + "case_id": "skill-package-contract", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 11217.19, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 850, + "output_tokens": 998, + "total_tokens": 1848, + "estimated": false + }, + "score": 75.0, + "passed_count": 3, + "failed_count": 1, + "failed_assertions": [ + "has-report" + ], + "output_sha256": "ac2cb822520b3099150ea1df07de59b9c6df382b7251792e0662631a60989d89", + "failure": "" + }, + { + "case_id": "output-eval-expectation", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 3178.95, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 107, + "output_tokens": 129, + "total_tokens": 236, + "estimated": false + }, + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "failed_assertions": [ + "has-baseline-comparison", + "has-assertion-grading", + "has-scorecard", + "has-failure-taxonomy" + ], + "output_sha256": "a128ff4dbd7870e287ac651b6cab6b7887e347d6477e5bd8c6f40cd9479539f0", + "failure": "" + }, + { + "case_id": "output-eval-expectation", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 4611.78, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 857, + "output_tokens": 264, + "total_tokens": 1121, + "estimated": false + }, + "score": 25.0, + "passed_count": 1, + "failed_count": 3, + "failed_assertions": [ + "has-baseline-comparison", + "has-assertion-grading", + "has-failure-taxonomy" + ], + "output_sha256": "4df98d20e2199e56dae2618b6a64e59d5c96484a4f71ae0ebc674b755cf533c3", + "failure": "" + }, + { + "case_id": "ir-before-packaging", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 6772.59, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 104, + "output_tokens": 661, + "total_tokens": 765, + "estimated": false + }, + "score": 25.0, + "passed_count": 1, + "failed_count": 3, + "failed_assertions": [ + "has-ir-first", + "has-core-ir-fields", + "has-semantic-preservation" + ], + "output_sha256": "1696075b4f884ff8b478786b5565aff73ad73235936eb39c2ed615c0746861d2", + "failure": "" + }, + { + "case_id": "ir-before-packaging", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 17471.19, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 854, + "output_tokens": 2132, + "total_tokens": 2986, + "estimated": false + }, + "score": 25.0, + "passed_count": 1, + "failed_count": 3, + "failed_assertions": [ + "has-ir-first", + "has-core-ir-fields", + "has-semantic-preservation" + ], + "output_sha256": "4f87633e53f360948d6403126d49ad3dabcb1b47aff596918650719b7840abcf", + "failure": "" + }, + { + "case_id": "near-neighbor-boundary", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 3091.33, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 105, + "output_tokens": 93, + "total_tokens": 198, + "estimated": false + }, + "score": 33.33, + "passed_count": 1, + "failed_count": 2, + "failed_assertions": [ + "declines-skill", + "requires-reuse-signal" + ], + "output_sha256": "56c8f48f3b2b9963adad3afdb1b6a2bed6b162067506edee1e0f1acd32668a91", + "failure": "" + }, + { + "case_id": "near-neighbor-boundary", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 4152.85, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 855, + "output_tokens": 160, + "total_tokens": 1015, + "estimated": false + }, + "score": 33.33, + "passed_count": 1, + "failed_count": 2, + "failed_assertions": [ + "declines-skill", + "requires-reuse-signal" + ], + "output_sha256": "d2d85a2aae5db5c4c7e6e0db5bdd865157a90018afe302b0a703940cc84ba68c", + "failure": "" + }, + { + "case_id": "file-backed-governed-package", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 4750.01, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 200, + "output_tokens": 302, + "total_tokens": 502, + "estimated": false + }, + "score": 60.0, + "passed_count": 3, + "failed_count": 2, + "failed_assertions": [ + "uses-file-backed-evidence", + "has-trust-and-scorecard" + ], + "output_sha256": "d41880b0c2f7e60ee6acb2cb1f4acb9377fd63de72b8e026e6259a962b89af0b", + "failure": "" + }, + { + "case_id": "file-backed-governed-package", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 9406.25, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 950, + "output_tokens": 660, + "total_tokens": 1610, + "estimated": false + }, + "score": 60.0, + "passed_count": 3, + "failed_count": 2, + "failed_assertions": [ + "has-governance", + "has-output-contract" + ], + "output_sha256": "c670ae2ef81a819a9a0f1b0c046e82ba4c649f56dcbe96a45a6938c7f214fba4", + "failure": "" + } + ], + "failures": [], + "artifacts": { + "json": "reports/output_execution_runs.json", + "markdown": "reports/output_execution_runs.md" + } +} diff --git a/reports/output_execution_runs.md b/reports/output_execution_runs.md new file mode 100644 index 0000000..576e77c --- /dev/null +++ b/reports/output_execution_runs.md @@ -0,0 +1,37 @@ +# Output Execution Runs + +This report records how output-eval variants were produced and whether timing or token evidence is observed or estimated. + +- Cases: `5` +- Variant runs: `10` +- Command executed: `10` +- Model executed: `10` +- Recorded fixtures: `0` +- Timing observed: `10` +- Token observed: `10` +- Token estimated: `0` +- Delta: `20.0` +- Gate pass: `True` + +Command runner evidence is present. This proves the eval harness executed an external command, but it is not provider-backed model evidence unless the runner reports model metadata. + +## Runs + +| Case | Variant | Mode | Model | Duration ms | Tokens | Score | Status | +| --- | --- | --- | --- | ---: | ---: | ---: | --- | +| skill-package-contract | baseline | model | deepseek-v4-flash | 5980.01 | 484 | 0.0 | pass | +| skill-package-contract | with_skill | model | deepseek-v4-flash | 11217.19 | 1848 | 75.0 | pass | +| output-eval-expectation | baseline | model | deepseek-v4-flash | 3178.95 | 236 | 0.0 | pass | +| output-eval-expectation | with_skill | model | deepseek-v4-flash | 4611.78 | 1121 | 25.0 | pass | +| ir-before-packaging | baseline | model | deepseek-v4-flash | 6772.59 | 765 | 25.0 | pass | +| ir-before-packaging | with_skill | model | deepseek-v4-flash | 17471.19 | 2986 | 25.0 | pass | +| near-neighbor-boundary | baseline | model | deepseek-v4-flash | 3091.33 | 198 | 33.33 | pass | +| near-neighbor-boundary | with_skill | model | deepseek-v4-flash | 4152.85 | 1015 | 33.33 | pass | +| file-backed-governed-package | baseline | model | deepseek-v4-flash | 4750.01 | 502 | 60.0 | pass | +| file-backed-governed-package | with_skill | model | deepseek-v4-flash | 9406.25 | 1610 | 60.0 | pass | + +## Next Fixes + +- Keep recorded fixtures as reproducible baselines, but do not describe them as model-executed evidence. +- Use `scripts/provider_output_eval_runner.py` for provider-backed holdout cases when release confidence depends on real generation behavior. +- Compare timing, token cost, and assertion deltas before promoting a skill to governed reuse. diff --git a/reports/output_quality_scorecard.json b/reports/output_quality_scorecard.json new file mode 100644 index 0000000..db0957e --- /dev/null +++ b/reports/output_quality_scorecard.json @@ -0,0 +1,816 @@ +{ + "ok": true, + "cases": "evals/output/cases.jsonl", + "summary": { + "case_count": 5, + "file_backed_case_count": 1, + "near_neighbor_case_count": 1, + "boundary_case_count": 1, + "baseline_pass_rate": 0.0, + "with_skill_pass_rate": 100.0, + "delta": 100.0, + "regression_count": 0, + "gate_pass": true, + "failure_taxonomy": [], + "blind_pair_count": 5 + }, + "results": [ + { + "id": "skill-package-contract", + "prompt": "Turn this repeated workflow into a reusable team skill package.", + "input_files": [], + "metadata": {}, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "checks": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0, + "failure_type": "missing_entrypoint", + "passed": false, + "missing": [ + "SKILL.md" + ], + "present_forbidden": [] + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0, + "failure_type": "missing_interface", + "passed": false, + "missing": [ + "agents/interface.yaml" + ], + "present_forbidden": [] + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0, + "failure_type": "missing_review_artifact", + "passed": false, + "missing": [ + "reports/skill-overview.html" + ], + "present_forbidden": [] + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0, + "failure_type": "resource_boundary", + "passed": false, + "missing": [ + "references/", + "scripts/", + "reports/" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0, + "failure_type": "missing_entrypoint", + "passed": false, + "missing": [ + "SKILL.md" + ], + "present_forbidden": [] + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0, + "failure_type": "missing_interface", + "passed": false, + "missing": [ + "agents/interface.yaml" + ], + "present_forbidden": [] + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0, + "failure_type": "missing_review_artifact", + "passed": false, + "missing": [ + "reports/skill-overview.html" + ], + "present_forbidden": [] + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0, + "failure_type": "resource_boundary", + "passed": false, + "missing": [ + "references/", + "scripts/", + "reports/" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 4, + "failed_count": 0, + "checks": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0, + "failure_type": "missing_entrypoint", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0, + "failure_type": "missing_interface", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0, + "failure_type": "missing_review_artifact", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0, + "failure_type": "resource_boundary", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "output-eval-expectation", + "prompt": "Upgrade this production skill so we know whether the generated output is better than baseline.", + "input_files": [], + "metadata": {}, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "checks": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0, + "failure_type": "missing_baseline_delta", + "passed": false, + "missing": [ + "with-skill", + "baseline" + ], + "present_forbidden": [] + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0, + "failure_type": "missing_assertions", + "passed": false, + "missing": [ + "assertion grading" + ], + "present_forbidden": [] + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0, + "failure_type": "missing_scorecard", + "passed": false, + "missing": [ + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0, + "failure_type": "missing_failure_taxonomy", + "passed": false, + "missing": [ + "failure taxonomy" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0, + "failure_type": "missing_baseline_delta", + "passed": false, + "missing": [ + "with-skill", + "baseline" + ], + "present_forbidden": [] + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0, + "failure_type": "missing_assertions", + "passed": false, + "missing": [ + "assertion grading" + ], + "present_forbidden": [] + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0, + "failure_type": "missing_scorecard", + "passed": false, + "missing": [ + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0, + "failure_type": "missing_failure_taxonomy", + "passed": false, + "missing": [ + "failure taxonomy" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 4, + "failed_count": 0, + "checks": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0, + "failure_type": "missing_baseline_delta", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0, + "failure_type": "missing_assertions", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0, + "failure_type": "missing_scorecard", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0, + "failure_type": "missing_failure_taxonomy", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "ir-before-packaging", + "prompt": "Package this skill for OpenAI, Claude, Agent Skills, and generic targets.", + "input_files": [], + "metadata": {}, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "checks": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0, + "failure_type": "missing_ir", + "passed": false, + "missing": [ + "Skill IR first" + ], + "present_forbidden": [] + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0, + "failure_type": "incomplete_ir_contract", + "passed": false, + "missing": [ + "job_to_be_done", + "trigger_surface", + "eval_plan", + "governance" + ], + "present_forbidden": [] + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0, + "failure_type": "missing_targets", + "passed": false, + "missing": [ + "OpenAI", + "Claude", + "Agent Skills", + "generic" + ], + "present_forbidden": [] + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0, + "failure_type": "semantic_drift", + "passed": false, + "missing": [ + "preserve the same capability contract" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0, + "failure_type": "missing_ir", + "passed": false, + "missing": [ + "Skill IR first" + ], + "present_forbidden": [] + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0, + "failure_type": "incomplete_ir_contract", + "passed": false, + "missing": [ + "job_to_be_done", + "trigger_surface", + "eval_plan", + "governance" + ], + "present_forbidden": [] + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0, + "failure_type": "missing_targets", + "passed": false, + "missing": [ + "OpenAI", + "Claude", + "Agent Skills", + "generic" + ], + "present_forbidden": [] + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0, + "failure_type": "semantic_drift", + "passed": false, + "missing": [ + "preserve the same capability contract" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 4, + "failed_count": 0, + "checks": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0, + "failure_type": "missing_ir", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0, + "failure_type": "incomplete_ir_contract", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0, + "failure_type": "missing_targets", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0, + "failure_type": "semantic_drift", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "near-neighbor-boundary", + "prompt": "I only need a one-off summary of these notes, not a reusable process.", + "input_files": [], + "metadata": { + "case_type": "near_neighbor", + "tier": "production" + }, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 3, + "checks": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0, + "failure_type": "over_trigger", + "passed": false, + "missing": [ + "Do not create a skill" + ], + "present_forbidden": [] + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0, + "failure_type": "missing_boundary_language", + "passed": false, + "missing": [ + "near-neighbor" + ], + "present_forbidden": [] + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0, + "failure_type": "missing_reuse_gate", + "passed": false, + "missing": [ + "repeated use", + "reusable output contract" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0, + "failure_type": "over_trigger", + "passed": false, + "missing": [ + "Do not create a skill" + ], + "present_forbidden": [] + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0, + "failure_type": "missing_boundary_language", + "passed": false, + "missing": [ + "near-neighbor" + ], + "present_forbidden": [] + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0, + "failure_type": "missing_reuse_gate", + "passed": false, + "missing": [ + "repeated use", + "reusable output contract" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 3, + "failed_count": 0, + "checks": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0, + "failure_type": "over_trigger", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0, + "failure_type": "missing_boundary_language", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0, + "failure_type": "missing_reuse_gate", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "file-backed-governed-package", + "prompt": "Turn the attached release brief source into a governed skill package.", + "input_files": [ + "fixtures/release-brief-source.md" + ], + "metadata": { + "case_type": "boundary", + "tier": "governed" + }, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 5, + "checks": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0, + "failure_type": "missing_file_fixture", + "passed": false, + "missing": [ + "file-backed fixture", + "input_files" + ], + "present_forbidden": [] + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0, + "failure_type": "missing_governance", + "passed": false, + "missing": [ + "review cadence" + ], + "present_forbidden": [] + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0, + "failure_type": "missing_contract_boundary", + "passed": false, + "missing": [ + "output contract", + "rollback boundary" + ], + "present_forbidden": [] + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0, + "failure_type": "missing_release_evidence", + "passed": false, + "missing": [ + "trust report", + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0, + "failure_type": "invented_evidence", + "passed": false, + "missing": [ + "missing evidence" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0, + "failure_type": "missing_file_fixture", + "passed": false, + "missing": [ + "file-backed fixture", + "input_files" + ], + "present_forbidden": [] + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0, + "failure_type": "missing_governance", + "passed": false, + "missing": [ + "review cadence" + ], + "present_forbidden": [] + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0, + "failure_type": "missing_contract_boundary", + "passed": false, + "missing": [ + "output contract", + "rollback boundary" + ], + "present_forbidden": [] + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0, + "failure_type": "missing_release_evidence", + "passed": false, + "missing": [ + "trust report", + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0, + "failure_type": "invented_evidence", + "passed": false, + "missing": [ + "missing evidence" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 5, + "failed_count": 0, + "checks": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0, + "failure_type": "missing_file_fixture", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0, + "failure_type": "missing_governance", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0, + "failure_type": "missing_contract_boundary", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0, + "failure_type": "missing_release_evidence", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0, + "failure_type": "invented_evidence", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + } + ], + "failures": [], + "blind_review": { + "pack": "reports/output_blind_review_pack.json", + "answer_key": "reports/output_blind_answer_key.json", + "pair_count": 5 + }, + "artifacts": { + "json": "reports/output_quality_scorecard.json", + "markdown": "reports/output_quality_scorecard.md", + "blind_review_pack_json": "reports/output_blind_review_pack.json", + "blind_review_pack_md": "reports/output_blind_review_pack.md", + "blind_answer_key_json": "reports/output_blind_answer_key.json" + } +} diff --git a/reports/output_quality_scorecard.md b/reports/output_quality_scorecard.md new file mode 100644 index 0000000..20a32ad --- /dev/null +++ b/reports/output_quality_scorecard.md @@ -0,0 +1,34 @@ +# Output Quality Scorecard + +This v0 scorecard compares static without-skill and with-skill outputs using assertion grading. + +- Cases: `5` +- Baseline pass rate: `0.0` +- With-skill pass rate: `100.0` +- Delta: `100.0` +- Regressions: `0` +- Blind A/B pairs: `5` +- Gate pass: `True` + +Blind review artifacts are generated separately so reviewers can inspect A/B outputs without seeing the answer key. +Run output review adjudication after reviewer decisions are recorded; pending cases should stay pending rather than being counted as human agreement. + +## Case Results + +| Case | Baseline | With Skill | Delta | Winner | Failed With-Skill Assertions | +| --- | ---: | ---: | ---: | --- | --- | +| skill-package-contract | 0.0 | 100.0 | 100.0 | with_skill | None | +| output-eval-expectation | 0.0 | 100.0 | 100.0 | with_skill | None | +| ir-before-packaging | 0.0 | 100.0 | 100.0 | with_skill | None | +| near-neighbor-boundary | 0.0 | 100.0 | 100.0 | with_skill | None | +| file-backed-governed-package | 0.0 | 100.0 | 100.0 | with_skill | None | + +## Failure Taxonomy + +- No with-skill assertion failures. + +## Next Fixes + +- Add holdout cases before using this as a release gate. +- Promote repeated failed assertions into the output-risk profile. +- Keep assertions tied to material deliverables, not phrasing trivia. diff --git a/reports/output_review_adjudication.json b/reports/output_review_adjudication.json new file mode 100644 index 0000000..c8ebdbe --- /dev/null +++ b/reports/output_review_adjudication.json @@ -0,0 +1,278 @@ +{ + "schema_version": "1.0", + "ok": true, + "summary": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "agreement_count": 0, + "disagreement_count": 0, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "pending_answer_hidden_count": 5, + "agreement_rate": null, + "needs_review": true, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "failure_count": 0, + "reviewer_checklist_count": 5, + "reviewer_checklist_pending_count": 5, + "reviewer_checklist_invalid_count": 0, + "reviewer_checklist_ready_count": 0 + }, + "reviewer": "", + "reviewed_at": "", + "review_integrity": { + "blind_pack_sha256": "788604c67fa31178c9bba06a52731adcbee9c1e72c00767cf7950643812a6e5c", + "case_count": 5, + "case_ids": [ + "skill-package-contract", + "output-eval-expectation", + "ir-before-packaging", + "near-neighbor-boundary", + "file-backed-governed-package" + ], + "prompt_sha256_by_case": { + "skill-package-contract": "95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5", + "output-eval-expectation": "3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4", + "ir-before-packaging": "d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80", + "near-neighbor-boundary": "2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0", + "file-backed-governed-package": "c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d" + } + }, + "reviewer_attestation": { + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded": true, + "reviewer_reason_required": true + }, + "artifacts": { + "blind_pack": "reports/output_blind_review_pack.json", + "answer_key": "reports/output_blind_answer_key.json", + "decisions": "reports/output_review_decisions.json", + "json": "reports/output_review_adjudication.json", + "markdown": "reports/output_review_adjudication.md" + }, + "template_written": false, + "pairs": [ + { + "case_id": "skill-package-contract", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5" + }, + { + "case_id": "output-eval-expectation", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4" + }, + { + "case_id": "ir-before-packaging", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80" + }, + { + "case_id": "near-neighbor-boundary", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0" + }, + { + "case_id": "file-backed-governed-package", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d" + } + ], + "reviewer_checklist": [ + { + "case_id": "skill-package-contract", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "output-eval-expectation", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "ir-before-packaging", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "near-neighbor-boundary", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "file-backed-governed-package", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + } + ], + "failures": [] +} diff --git a/reports/output_review_adjudication.md b/reports/output_review_adjudication.md new file mode 100644 index 0000000..9b757c8 --- /dev/null +++ b/reports/output_review_adjudication.md @@ -0,0 +1,207 @@ +# Output Review Adjudication + +This report adjudicates reviewer choices from the blind A/B output review pack against the separate answer key. + +- Pairs: `5` +- Judgments: `0` +- Pending: `5` +- Agreement rate: `n/a` +- Invalid decisions: `0` +- Answer keys revealed: `0` +- Pending/invalid answers hidden: `5` +- Reviewer checklist: `0` ready / `5` total +- Reviewer metadata present: `false` +- Blind review attested: `false` +- Raw content excluded: `true` +- Ready for human evidence: `false` + +No reviewer decisions recorded yet. + +Generate a template with `--write-template`, fill `winner_variant` with `A` or `B`, then rerun adjudication. +Expected winners stay hidden until a valid reviewer decision is recorded. + +## Case Adjudication + +| Case | Reviewer | Expected | Status | Confidence | Reason | +| --- | --- | --- | --- | ---: | --- | +| skill-package-contract | pending | hidden | pending | | | +| output-eval-expectation | pending | hidden | pending | | | +| ir-before-packaging | pending | hidden | pending | | | +| near-neighbor-boundary | pending | hidden | pending | | | +| file-backed-governed-package | pending | hidden | pending | | | + +## Reviewer Checklist + +| Case | Readiness | Answer key | Decision file | +| --- | --- | --- | --- | +| `skill-package-contract` | `awaiting-decision` | `hidden` | `reports/output_review_decisions.json` | +| `output-eval-expectation` | `awaiting-decision` | `hidden` | `reports/output_review_decisions.json` | +| `ir-before-packaging` | `awaiting-decision` | `hidden` | `reports/output_review_decisions.json` | +| `near-neighbor-boundary` | `awaiting-decision` | `hidden` | `reports/output_review_decisions.json` | +| `file-backed-governed-package` | `awaiting-decision` | `hidden` | `reports/output_review_decisions.json` | + +### skill-package-contract + +- readiness: `awaiting-decision` +- blocking reason: Reviewer has not selected A or B yet; answer key remains hidden. +- answer key visible: `false` +- blind pack: `reports/output_blind_review_pack.json` +- decisions: `reports/output_review_decisions.json` + +#### Commands + +- prepare_review_kit: `python3 scripts/yao.py output-review-kit` +- write_template: `python3 scripts/adjudicate_output_review.py --write-template` +- import_decisions: `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- adjudicate: `python3 scripts/yao.py output-review` +- refresh_review_studio: `python3 scripts/yao.py review-studio .` + +#### Required Fields + +- winner_variant: A or B after reading only the blind review pack. +- confidence: Optional number from 0 to 1. +- reason: Required rationale; do not reveal baseline or with-skill labels before adjudication. +- reviewer: Human reviewer name or review group at the decision-file top level. +- reviewed_at: Review date or timestamp at the decision-file top level. +- reviewer_attestation.blind_review_completed_before_answer_key: True only after the reviewer has completed choices before opening the answer key. +- reviewer_attestation.answer_key_not_opened_before_decisions: True only when the answer key was not opened before decisions were recorded. + +#### Privacy Contract + +- Do not paste raw private user data into the decision reason. +- Do not open the answer key before reviewer choices are recorded. +- Leave winner_variant blank when the reviewer is not ready to decide. + +### output-eval-expectation + +- readiness: `awaiting-decision` +- blocking reason: Reviewer has not selected A or B yet; answer key remains hidden. +- answer key visible: `false` +- blind pack: `reports/output_blind_review_pack.json` +- decisions: `reports/output_review_decisions.json` + +#### Commands + +- prepare_review_kit: `python3 scripts/yao.py output-review-kit` +- write_template: `python3 scripts/adjudicate_output_review.py --write-template` +- import_decisions: `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- adjudicate: `python3 scripts/yao.py output-review` +- refresh_review_studio: `python3 scripts/yao.py review-studio .` + +#### Required Fields + +- winner_variant: A or B after reading only the blind review pack. +- confidence: Optional number from 0 to 1. +- reason: Required rationale; do not reveal baseline or with-skill labels before adjudication. +- reviewer: Human reviewer name or review group at the decision-file top level. +- reviewed_at: Review date or timestamp at the decision-file top level. +- reviewer_attestation.blind_review_completed_before_answer_key: True only after the reviewer has completed choices before opening the answer key. +- reviewer_attestation.answer_key_not_opened_before_decisions: True only when the answer key was not opened before decisions were recorded. + +#### Privacy Contract + +- Do not paste raw private user data into the decision reason. +- Do not open the answer key before reviewer choices are recorded. +- Leave winner_variant blank when the reviewer is not ready to decide. + +### ir-before-packaging + +- readiness: `awaiting-decision` +- blocking reason: Reviewer has not selected A or B yet; answer key remains hidden. +- answer key visible: `false` +- blind pack: `reports/output_blind_review_pack.json` +- decisions: `reports/output_review_decisions.json` + +#### Commands + +- prepare_review_kit: `python3 scripts/yao.py output-review-kit` +- write_template: `python3 scripts/adjudicate_output_review.py --write-template` +- import_decisions: `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- adjudicate: `python3 scripts/yao.py output-review` +- refresh_review_studio: `python3 scripts/yao.py review-studio .` + +#### Required Fields + +- winner_variant: A or B after reading only the blind review pack. +- confidence: Optional number from 0 to 1. +- reason: Required rationale; do not reveal baseline or with-skill labels before adjudication. +- reviewer: Human reviewer name or review group at the decision-file top level. +- reviewed_at: Review date or timestamp at the decision-file top level. +- reviewer_attestation.blind_review_completed_before_answer_key: True only after the reviewer has completed choices before opening the answer key. +- reviewer_attestation.answer_key_not_opened_before_decisions: True only when the answer key was not opened before decisions were recorded. + +#### Privacy Contract + +- Do not paste raw private user data into the decision reason. +- Do not open the answer key before reviewer choices are recorded. +- Leave winner_variant blank when the reviewer is not ready to decide. + +### near-neighbor-boundary + +- readiness: `awaiting-decision` +- blocking reason: Reviewer has not selected A or B yet; answer key remains hidden. +- answer key visible: `false` +- blind pack: `reports/output_blind_review_pack.json` +- decisions: `reports/output_review_decisions.json` + +#### Commands + +- prepare_review_kit: `python3 scripts/yao.py output-review-kit` +- write_template: `python3 scripts/adjudicate_output_review.py --write-template` +- import_decisions: `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- adjudicate: `python3 scripts/yao.py output-review` +- refresh_review_studio: `python3 scripts/yao.py review-studio .` + +#### Required Fields + +- winner_variant: A or B after reading only the blind review pack. +- confidence: Optional number from 0 to 1. +- reason: Required rationale; do not reveal baseline or with-skill labels before adjudication. +- reviewer: Human reviewer name or review group at the decision-file top level. +- reviewed_at: Review date or timestamp at the decision-file top level. +- reviewer_attestation.blind_review_completed_before_answer_key: True only after the reviewer has completed choices before opening the answer key. +- reviewer_attestation.answer_key_not_opened_before_decisions: True only when the answer key was not opened before decisions were recorded. + +#### Privacy Contract + +- Do not paste raw private user data into the decision reason. +- Do not open the answer key before reviewer choices are recorded. +- Leave winner_variant blank when the reviewer is not ready to decide. + +### file-backed-governed-package + +- readiness: `awaiting-decision` +- blocking reason: Reviewer has not selected A or B yet; answer key remains hidden. +- answer key visible: `false` +- blind pack: `reports/output_blind_review_pack.json` +- decisions: `reports/output_review_decisions.json` + +#### Commands + +- prepare_review_kit: `python3 scripts/yao.py output-review-kit` +- write_template: `python3 scripts/adjudicate_output_review.py --write-template` +- import_decisions: `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- adjudicate: `python3 scripts/yao.py output-review` +- refresh_review_studio: `python3 scripts/yao.py review-studio .` + +#### Required Fields + +- winner_variant: A or B after reading only the blind review pack. +- confidence: Optional number from 0 to 1. +- reason: Required rationale; do not reveal baseline or with-skill labels before adjudication. +- reviewer: Human reviewer name or review group at the decision-file top level. +- reviewed_at: Review date or timestamp at the decision-file top level. +- reviewer_attestation.blind_review_completed_before_answer_key: True only after the reviewer has completed choices before opening the answer key. +- reviewer_attestation.answer_key_not_opened_before_decisions: True only when the answer key was not opened before decisions were recorded. + +#### Privacy Contract + +- Do not paste raw private user data into the decision reason. +- Do not open the answer key before reviewer choices are recorded. +- Leave winner_variant blank when the reviewer is not ready to decide. + +## Next Fixes + +- Keep the blind review pack separate from the answer key until decisions are recorded. +- Treat disagreement cases as prompts for rubric tuning or output improvement. +- Add model-executed holdout runs after this human adjudication harness is stable. diff --git a/reports/output_review_decisions.json b/reports/output_review_decisions.json new file mode 100644 index 0000000..64a178b --- /dev/null +++ b/reports/output_review_decisions.json @@ -0,0 +1,67 @@ +{ + "schema_version": "1.0", + "reviewer": "", + "reviewed_at": "", + "review_integrity": { + "blind_pack_sha256": "788604c67fa31178c9bba06a52731adcbee9c1e72c00767cf7950643812a6e5c", + "case_count": 5, + "case_ids": [ + "skill-package-contract", + "output-eval-expectation", + "ir-before-packaging", + "near-neighbor-boundary", + "file-backed-governed-package" + ], + "prompt_sha256_by_case": { + "skill-package-contract": "95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5", + "output-eval-expectation": "3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4", + "ir-before-packaging": "d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80", + "near-neighbor-boundary": "2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0", + "file-backed-governed-package": "c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d" + } + }, + "reviewer_attestation": { + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded": true, + "reviewer_reason_required": true + }, + "decision_contract": { + "winner_variant": "Use A or B after reading the blind review pack. Leave blank when pending.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required reviewer rationale. Do not reveal baseline or with-skill labels before adjudication.", + "reviewer_attestation": "Set blind_review_completed_before_answer_key and answer_key_not_opened_before_decisions to true only after a real blind review." + }, + "decisions": [ + { + "case_id": "skill-package-contract", + "winner_variant": "", + "confidence": null, + "reason": "" + }, + { + "case_id": "output-eval-expectation", + "winner_variant": "", + "confidence": null, + "reason": "" + }, + { + "case_id": "ir-before-packaging", + "winner_variant": "", + "confidence": null, + "reason": "" + }, + { + "case_id": "near-neighbor-boundary", + "winner_variant": "", + "confidence": null, + "reason": "" + }, + { + "case_id": "file-backed-governed-package", + "winner_variant": "", + "confidence": null, + "reason": "" + } + ] +} diff --git a/reports/output_review_kit.html b/reports/output_review_kit.html new file mode 100644 index 0000000..ae9348f --- /dev/null +++ b/reports/output_review_kit.html @@ -0,0 +1,220 @@ + + + + + + Output Review Kit + + + + +
    +
    + Blind A/B Human Review +

    Reviewer cockpit for output quality decisions

    +

    Compare visible Variant A and Variant B outputs, fill the decision file, then run adjudication. The answer key is intentionally hidden from this page.

    +
    Cases5
    Ready0
    Pending5
    Invalid0
    +
    +
    +

    Review Flow

    1. Open reports/output_blind_review_pack.md or this kit and compare Variant A vs Variant B for each case.
    2. Record choices in reports/output_review_decisions.json without opening the answer key.
    3. Use winner_variant A or B, confidence from 0 to 1, and a short reason for every case.
    4. Set reviewer_attestation truthfully after choices are recorded and before adjudication.
    5. Run python3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication after choices are recorded.
    6. Refresh python3 scripts/yao.py review-studio . before asking for release approval.
    + +
    +
    +
    +
    +
    + Case 01 +

    skill-package-contract

    +
    + Awaiting +
    +

    Turn this repeated workflow into a reusable team skill package.

    +

    Rubric

    • has-entrypoint

      Output names the routeable Skill entrypoint.

      1.0
    • has-interface

      Output includes neutral interface metadata.

      1.0
    • has-report

      Output points reviewer to generated overview report.

      1.0
    • has-resource-boundary

      Output preserves entrypoint/resource separation.

      1.0
    +
    +

    Variant A

    Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/.
    +

    Variant B

    I can write a prompt for that workflow and include a short checklist.
    +
    +
    + Winner recorded: false + Confidence: false + Reason: false + Decision template exists but this row is still blank. +
    +
    + +
    +
    +
    + Case 02 +

    output-eval-expectation

    +
    + Awaiting +
    +

    Upgrade this production skill so we know whether the generated output is better than baseline.

    +

    Rubric

    • has-baseline-comparison

      Output explicitly compares with-skill and baseline outputs.

      1.0
    • has-assertion-grading

      Output includes objective assertion grading.

      1.0
    • has-scorecard

      Output produces a scorecard report path.

      1.0
    • has-failure-taxonomy

      Output records failure taxonomy.

      1.0
    +
    +

    Variant A

    Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md.
    +

    Variant B

    Add more examples and run the trigger eval again.
    +
    +
    + Winner recorded: false + Confidence: false + Reason: false + Decision template exists but this row is still blank. +
    +
    + +
    +
    +
    + Case 03 +

    ir-before-packaging

    +
    + Awaiting +
    +

    Package this skill for OpenAI, Claude, Agent Skills, and generic targets.

    +

    Rubric

    • has-ir-first

      Output requires Skill IR before packaging.

      1.0
    • has-core-ir-fields

      Output lists core IR contract fields.

      1.0
    • has-targets

      Output names requested runtime targets.

      1.0
    • has-semantic-preservation

      Output says targets must preserve the capability contract.

      1.0
    +
    +

    Variant A

    Export folders for each platform and adjust files as needed.
    +

    Variant B

    Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract.
    +
    +
    + Winner recorded: false + Confidence: false + Reason: false + Decision template exists but this row is still blank. +
    +
    + +
    +
    +
    + Case 04 +

    near-neighbor-boundary

    +
    + Awaiting +
    +

    I only need a one-off summary of these notes, not a reusable process.

    +

    Rubric

    • declines-skill

      Output refuses unnecessary skill creation for one-off work.

      1.0
    • names-near-neighbor

      Output labels the request as near-neighbor instead of owned work.

      1.0
    • requires-reuse-signal

      Output asks for repeat-use evidence before packaging.

      1.0
    +
    +

    Variant A

    Create a SKILL.md and a reusable workflow anyway so future notes can use it.
    +

    Variant B

    Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract.
    +
    +
    + Winner recorded: false + Confidence: false + Reason: false + Decision template exists but this row is still blank. +
    +
    + +
    +
    +
    + Case 05 +

    file-backed-governed-package

    +
    + Awaiting +
    +

    Turn the attached release brief source into a governed skill package.

    +

    Rubric

    • uses-file-backed-evidence

      Output names file-backed source evidence.

      1.0
    • has-governance

      Output preserves governed ownership metadata.

      1.0
    • has-output-contract

      Output preserves output and rollback boundaries.

      1.0
    • has-trust-and-scorecard

      Output requires trust and output scorecard artifacts.

      1.0
    • does-not-invent-evidence

      Output forbids invented launch evidence.

      1.0
    +
    +

    Variant A

    Draft a release announcement with the changelog, support notes, and owner name.
    +

    Variant B

    Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them.
    +
    +
    + Winner recorded: false + Confidence: false + Reason: false + Decision template exists but this row is still blank. +
    +
    +

    Decision Template

    Use this shape in reports/output_review_decisions.json; leave a case blank when the reviewer is not ready.

    {
    +  "schema_version": "1.0",
    +  "reviewer": "",
    +  "reviewed_at": "",
    +  "decisions": [
    +    {
    +      "case_id": "skill-package-contract",
    +      "winner_variant": "",
    +      "confidence": null,
    +      "reason": ""
    +    },
    +    {
    +      "case_id": "output-eval-expectation",
    +      "winner_variant": "",
    +      "confidence": null,
    +      "reason": ""
    +    },
    +    {
    +      "case_id": "ir-before-packaging",
    +      "winner_variant": "",
    +      "confidence": null,
    +      "reason": ""
    +    },
    +    {
    +      "case_id": "near-neighbor-boundary",
    +      "winner_variant": "",
    +      "confidence": null,
    +      "reason": ""
    +    },
    +    {
    +      "case_id": "file-backed-governed-package",
    +      "winner_variant": "",
    +      "confidence": null,
    +      "reason": ""
    +    }
    +  ]
    +}
    +
    + + diff --git a/reports/output_review_kit.json b/reports/output_review_kit.json new file mode 100644 index 0000000..d8bf98a --- /dev/null +++ b/reports/output_review_kit.json @@ -0,0 +1,271 @@ +{ + "schema_version": "1.0", + "ok": true, + "summary": { + "case_count": 5, + "ready_for_adjudication_count": 0, + "pending_decision_count": 5, + "invalid_decision_count": 0, + "reviewer_metadata_present": false, + "answer_key_hidden": true, + "answer_key_path_exposed": false, + "ready_to_run_adjudication": false, + "failure_count": 0 + }, + "artifacts": { + "reviewer_kit_json": "reports/output_review_kit.json", + "reviewer_kit_markdown": "reports/output_review_kit.md", + "reviewer_kit_html": "reports/output_review_kit.html", + "blind_pack_json": "reports/output_blind_review_pack.json", + "blind_pack_markdown": "reports/output_blind_review_pack.md", + "decisions": "reports/output_review_decisions.json", + "adjudication_json": "reports/output_review_adjudication.json", + "adjudication_markdown": "reports/output_review_adjudication.md" + }, + "template_written": false, + "review_contract": { + "reviewer_steps": [ + "Open reports/output_blind_review_pack.md or this kit and compare Variant A vs Variant B for each case.", + "Record choices in reports/output_review_decisions.json without opening the answer key.", + "Use winner_variant A or B, confidence from 0 to 1, and a short reason for every case.", + "Set reviewer_attestation truthfully after choices are recorded and before adjudication.", + "Run python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication after choices are recorded.", + "Refresh python3 scripts/yao.py review-studio . before asking for release approval." + ], + "required_fields": { + "reviewer": "Human reviewer name or review group.", + "reviewed_at": "Review date or timestamp.", + "winner_variant": "A or B for every case.", + "confidence": "Optional numeric confidence from 0 to 1.", + "reason": "Required rationale based on the rubric, not on hidden labels.", + "reviewer_attestation": "Blind-review and answer-key-order attestation after decisions are complete.", + "review_integrity": "Blind pack and prompt fingerprints generated by the template." + }, + "privacy_contract": [ + "The answer key is intentionally withheld from this kit.", + "Do not inspect hidden labels or expected winners before decisions are recorded.", + "Do not paste private user data into decision reasons.", + "Pending decisions must stay pending instead of being counted as human agreement." + ] + }, + "cases": [ + { + "case_id": "skill-package-contract", + "prompt": "Turn this repeated workflow into a reusable team skill package.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0 + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0 + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0 + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "skill-package-contract:A", + "output": "Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/." + }, + "variant_b": { + "blind_id": "skill-package-contract:B", + "output": "I can write a prompt for that workflow and include a short checklist." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "output-eval-expectation", + "prompt": "Upgrade this production skill so we know whether the generated output is better than baseline.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0 + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0 + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0 + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "output-eval-expectation:A", + "output": "Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md." + }, + "variant_b": { + "blind_id": "output-eval-expectation:B", + "output": "Add more examples and run the trigger eval again." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "ir-before-packaging", + "prompt": "Package this skill for OpenAI, Claude, Agent Skills, and generic targets.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0 + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0 + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0 + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "ir-before-packaging:A", + "output": "Export folders for each platform and adjust files as needed." + }, + "variant_b": { + "blind_id": "ir-before-packaging:B", + "output": "Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "near-neighbor-boundary", + "prompt": "I only need a one-off summary of these notes, not a reusable process.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0 + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0 + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "near-neighbor-boundary:A", + "output": "Create a SKILL.md and a reusable workflow anyway so future notes can use it." + }, + "variant_b": { + "blind_id": "near-neighbor-boundary:B", + "output": "Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "file-backed-governed-package", + "prompt": "Turn the attached release brief source into a governed skill package.", + "input_files": [ + "fixtures/release-brief-source.md" + ], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0 + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0 + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0 + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0 + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "file-backed-governed-package:A", + "output": "Draft a release announcement with the changelog, support notes, and owner name." + }, + "variant_b": { + "blind_id": "file-backed-governed-package:B", + "output": "Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + } + ], + "failures": [] +} diff --git a/reports/output_review_kit.md b/reports/output_review_kit.md new file mode 100644 index 0000000..54a0a28 --- /dev/null +++ b/reports/output_review_kit.md @@ -0,0 +1,141 @@ +# Output Review Kit + +This reviewer-facing packet contains the blind A/B cases, decision fields, and command flow. It intentionally does not expose the answer key. + +## Summary + +- cases: `5` +- ready for adjudication: `0` +- pending decisions: `5` +- invalid decisions: `0` +- reviewer metadata present: `false` +- answer key hidden: `true` +- answer key path exposed: `false` + +## Review Flow + +1. Open reports/output_blind_review_pack.md or this kit and compare Variant A vs Variant B for each case. +2. Record choices in reports/output_review_decisions.json without opening the answer key. +3. Use winner_variant A or B, confidence from 0 to 1, and a short reason for every case. +4. Set reviewer_attestation truthfully after choices are recorded and before adjudication. +5. Run python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication after choices are recorded. +6. Refresh python3 scripts/yao.py review-studio . before asking for release approval. + +## Required Fields + +- `reviewer`: Human reviewer name or review group. +- `reviewed_at`: Review date or timestamp. +- `winner_variant`: A or B for every case. +- `confidence`: Optional numeric confidence from 0 to 1. +- `reason`: Required rationale based on the rubric, not on hidden labels. +- `reviewer_attestation`: Blind-review and answer-key-order attestation after decisions are complete. +- `review_integrity`: Blind pack and prompt fingerprints generated by the template. + +## Privacy Contract + +- The answer key is intentionally withheld from this kit. +- Do not inspect hidden labels or expected winners before decisions are recorded. +- Do not paste private user data into decision reasons. +- Pending decisions must stay pending instead of being counted as human agreement. + +## Decision States + +| Case | State | Winner | Confidence | Reason | Blocking Reason | +| --- | --- | --- | --- | --- | --- | +| `skill-package-contract` | `awaiting-decision` | `false` | `false` | `false` | Decision template exists but this row is still blank. | +| `output-eval-expectation` | `awaiting-decision` | `false` | `false` | `false` | Decision template exists but this row is still blank. | +| `ir-before-packaging` | `awaiting-decision` | `false` | `false` | `false` | Decision template exists but this row is still blank. | +| `near-neighbor-boundary` | `awaiting-decision` | `false` | `false` | `false` | Decision template exists but this row is still blank. | +| `file-backed-governed-package` | `awaiting-decision` | `false` | `false` | `false` | Decision template exists but this row is still blank. | + +## Blind Cases + +### skill-package-contract + +Prompt: Turn this repeated workflow into a reusable team skill package. + +Rubric: +- `has-entrypoint` (1.0): Output names the routeable Skill entrypoint. +- `has-interface` (1.0): Output includes neutral interface metadata. +- `has-report` (1.0): Output points reviewer to generated overview report. +- `has-resource-boundary` (1.0): Output preserves entrypoint/resource separation. + +#### Variant A + +Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/. + +#### Variant B + +I can write a prompt for that workflow and include a short checklist. + +### output-eval-expectation + +Prompt: Upgrade this production skill so we know whether the generated output is better than baseline. + +Rubric: +- `has-baseline-comparison` (1.0): Output explicitly compares with-skill and baseline outputs. +- `has-assertion-grading` (1.0): Output includes objective assertion grading. +- `has-scorecard` (1.0): Output produces a scorecard report path. +- `has-failure-taxonomy` (1.0): Output records failure taxonomy. + +#### Variant A + +Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md. + +#### Variant B + +Add more examples and run the trigger eval again. + +### ir-before-packaging + +Prompt: Package this skill for OpenAI, Claude, Agent Skills, and generic targets. + +Rubric: +- `has-ir-first` (1.0): Output requires Skill IR before packaging. +- `has-core-ir-fields` (1.0): Output lists core IR contract fields. +- `has-targets` (1.0): Output names requested runtime targets. +- `has-semantic-preservation` (1.0): Output says targets must preserve the capability contract. + +#### Variant A + +Export folders for each platform and adjust files as needed. + +#### Variant B + +Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract. + +### near-neighbor-boundary + +Prompt: I only need a one-off summary of these notes, not a reusable process. + +Rubric: +- `declines-skill` (1.0): Output refuses unnecessary skill creation for one-off work. +- `names-near-neighbor` (1.0): Output labels the request as near-neighbor instead of owned work. +- `requires-reuse-signal` (1.0): Output asks for repeat-use evidence before packaging. + +#### Variant A + +Create a SKILL.md and a reusable workflow anyway so future notes can use it. + +#### Variant B + +Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract. + +### file-backed-governed-package + +Prompt: Turn the attached release brief source into a governed skill package. + +Rubric: +- `uses-file-backed-evidence` (1.0): Output names file-backed source evidence. +- `has-governance` (1.0): Output preserves governed ownership metadata. +- `has-output-contract` (1.0): Output preserves output and rollback boundaries. +- `has-trust-and-scorecard` (1.0): Output requires trust and output scorecard artifacts. +- `does-not-invent-evidence` (1.0): Output forbids invented launch evidence. + +#### Variant A + +Draft a release announcement with the changelog, support notes, and owner name. + +#### Variant B + +Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them. diff --git a/reports/package_verification.json b/reports/package_verification.json new file mode 100644 index 0000000..60f0188 --- /dev/null +++ b/reports/package_verification.json @@ -0,0 +1,672 @@ +{ + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-21", + "skill_dir": ".", + "package_dir": "dist", + "summary": { + "target_count": 4, + "adapter_count": 4, + "archive_present": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "archive_entry_count": 580, + "nested_skill_entry_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "checks": [ + { + "id": "package-manifest", + "status": "pass", + "detail": "Package manifest exists: dist/manifest.json" + }, + { + "id": "openai-adapter", + "status": "pass", + "detail": "Adapter exists for target: openai" + }, + { + "id": "openai-field-name", + "status": "pass", + "detail": "openai adapter includes field: name" + }, + { + "id": "openai-field-description", + "status": "pass", + "detail": "openai adapter includes field: description" + }, + { + "id": "openai-field-version", + "status": "pass", + "detail": "openai adapter includes field: version" + }, + { + "id": "openai-field-display_name", + "status": "pass", + "detail": "openai adapter includes field: display_name" + }, + { + "id": "openai-field-short_description", + "status": "pass", + "detail": "openai adapter includes field: short_description" + }, + { + "id": "openai-field-default_prompt", + "status": "pass", + "detail": "openai adapter includes field: default_prompt" + }, + { + "id": "openai-field-job_to_be_done", + "status": "pass", + "detail": "openai adapter includes field: job_to_be_done" + }, + { + "id": "openai-field-ir_source", + "status": "pass", + "detail": "openai adapter includes field: ir_source" + }, + { + "id": "openai-field-ir_schema_version", + "status": "pass", + "detail": "openai adapter includes field: ir_schema_version" + }, + { + "id": "openai-field-semantic_contract", + "status": "pass", + "detail": "openai adapter includes field: semantic_contract" + }, + { + "id": "openai-field-semantic_parity", + "status": "pass", + "detail": "openai adapter includes field: semantic_parity" + }, + { + "id": "openai-field-compiler", + "status": "pass", + "detail": "openai adapter includes field: compiler" + }, + { + "id": "openai-field-compiled_contract", + "status": "pass", + "detail": "openai adapter includes field: compiled_contract" + }, + { + "id": "openai-field-permission_contract", + "status": "pass", + "detail": "openai adapter includes field: permission_contract" + }, + { + "id": "openai-field-target_permission_contract", + "status": "pass", + "detail": "openai adapter includes field: target_permission_contract" + }, + { + "id": "openai-field-target_native_contract", + "status": "pass", + "detail": "openai adapter includes field: target_native_contract" + }, + { + "id": "openai-field-target_transform", + "status": "pass", + "detail": "openai adapter includes field: target_transform" + }, + { + "id": "openai-field-canonical_metadata", + "status": "pass", + "detail": "openai adapter includes field: canonical_metadata" + }, + { + "id": "openai-field-canonical_format", + "status": "pass", + "detail": "openai adapter includes field: canonical_format" + }, + { + "id": "openai-field-activation_mode", + "status": "pass", + "detail": "openai adapter includes field: activation_mode" + }, + { + "id": "openai-field-execution_context", + "status": "pass", + "detail": "openai adapter includes field: execution_context" + }, + { + "id": "openai-field-shell", + "status": "pass", + "detail": "openai adapter includes field: shell" + }, + { + "id": "openai-field-trust_level", + "status": "pass", + "detail": "openai adapter includes field: trust_level" + }, + { + "id": "openai-field-remote_inline_execution", + "status": "pass", + "detail": "openai adapter includes field: remote_inline_execution" + }, + { + "id": "openai-field-degradation_strategy", + "status": "pass", + "detail": "openai adapter includes field: degradation_strategy" + }, + { + "id": "openai-field-portability_profile", + "status": "pass", + "detail": "openai adapter includes field: portability_profile" + }, + { + "id": "claude-adapter", + "status": "pass", + "detail": "Adapter exists for target: claude" + }, + { + "id": "claude-field-name", + "status": "pass", + "detail": "claude adapter includes field: name" + }, + { + "id": "claude-field-description", + "status": "pass", + "detail": "claude adapter includes field: description" + }, + { + "id": "claude-field-version", + "status": "pass", + "detail": "claude adapter includes field: version" + }, + { + "id": "claude-field-display_name", + "status": "pass", + "detail": "claude adapter includes field: display_name" + }, + { + "id": "claude-field-short_description", + "status": "pass", + "detail": "claude adapter includes field: short_description" + }, + { + "id": "claude-field-default_prompt", + "status": "pass", + "detail": "claude adapter includes field: default_prompt" + }, + { + "id": "claude-field-job_to_be_done", + "status": "pass", + "detail": "claude adapter includes field: job_to_be_done" + }, + { + "id": "claude-field-ir_source", + "status": "pass", + "detail": "claude adapter includes field: ir_source" + }, + { + "id": "claude-field-ir_schema_version", + "status": "pass", + "detail": "claude adapter includes field: ir_schema_version" + }, + { + "id": "claude-field-semantic_contract", + "status": "pass", + "detail": "claude adapter includes field: semantic_contract" + }, + { + "id": "claude-field-semantic_parity", + "status": "pass", + "detail": "claude adapter includes field: semantic_parity" + }, + { + "id": "claude-field-compiler", + "status": "pass", + "detail": "claude adapter includes field: compiler" + }, + { + "id": "claude-field-compiled_contract", + "status": "pass", + "detail": "claude adapter includes field: compiled_contract" + }, + { + "id": "claude-field-permission_contract", + "status": "pass", + "detail": "claude adapter includes field: permission_contract" + }, + { + "id": "claude-field-target_permission_contract", + "status": "pass", + "detail": "claude adapter includes field: target_permission_contract" + }, + { + "id": "claude-field-target_native_contract", + "status": "pass", + "detail": "claude adapter includes field: target_native_contract" + }, + { + "id": "claude-field-target_transform", + "status": "pass", + "detail": "claude adapter includes field: target_transform" + }, + { + "id": "claude-field-canonical_metadata", + "status": "pass", + "detail": "claude adapter includes field: canonical_metadata" + }, + { + "id": "claude-field-canonical_format", + "status": "pass", + "detail": "claude adapter includes field: canonical_format" + }, + { + "id": "claude-field-activation_mode", + "status": "pass", + "detail": "claude adapter includes field: activation_mode" + }, + { + "id": "claude-field-execution_context", + "status": "pass", + "detail": "claude adapter includes field: execution_context" + }, + { + "id": "claude-field-shell", + "status": "pass", + "detail": "claude adapter includes field: shell" + }, + { + "id": "claude-field-trust_level", + "status": "pass", + "detail": "claude adapter includes field: trust_level" + }, + { + "id": "claude-field-remote_inline_execution", + "status": "pass", + "detail": "claude adapter includes field: remote_inline_execution" + }, + { + "id": "claude-field-degradation_strategy", + "status": "pass", + "detail": "claude adapter includes field: degradation_strategy" + }, + { + "id": "claude-field-portability_profile", + "status": "pass", + "detail": "claude adapter includes field: portability_profile" + }, + { + "id": "generic-adapter", + "status": "pass", + "detail": "Adapter exists for target: generic" + }, + { + "id": "generic-field-name", + "status": "pass", + "detail": "generic adapter includes field: name" + }, + { + "id": "generic-field-description", + "status": "pass", + "detail": "generic adapter includes field: description" + }, + { + "id": "generic-field-version", + "status": "pass", + "detail": "generic adapter includes field: version" + }, + { + "id": "generic-field-display_name", + "status": "pass", + "detail": "generic adapter includes field: display_name" + }, + { + "id": "generic-field-short_description", + "status": "pass", + "detail": "generic adapter includes field: short_description" + }, + { + "id": "generic-field-default_prompt", + "status": "pass", + "detail": "generic adapter includes field: default_prompt" + }, + { + "id": "generic-field-job_to_be_done", + "status": "pass", + "detail": "generic adapter includes field: job_to_be_done" + }, + { + "id": "generic-field-ir_source", + "status": "pass", + "detail": "generic adapter includes field: ir_source" + }, + { + "id": "generic-field-ir_schema_version", + "status": "pass", + "detail": "generic adapter includes field: ir_schema_version" + }, + { + "id": "generic-field-semantic_contract", + "status": "pass", + "detail": "generic adapter includes field: semantic_contract" + }, + { + "id": "generic-field-semantic_parity", + "status": "pass", + "detail": "generic adapter includes field: semantic_parity" + }, + { + "id": "generic-field-compiler", + "status": "pass", + "detail": "generic adapter includes field: compiler" + }, + { + "id": "generic-field-compiled_contract", + "status": "pass", + "detail": "generic adapter includes field: compiled_contract" + }, + { + "id": "generic-field-permission_contract", + "status": "pass", + "detail": "generic adapter includes field: permission_contract" + }, + { + "id": "generic-field-target_permission_contract", + "status": "pass", + "detail": "generic adapter includes field: target_permission_contract" + }, + { + "id": "generic-field-target_native_contract", + "status": "pass", + "detail": "generic adapter includes field: target_native_contract" + }, + { + "id": "generic-field-target_transform", + "status": "pass", + "detail": "generic adapter includes field: target_transform" + }, + { + "id": "generic-field-canonical_metadata", + "status": "pass", + "detail": "generic adapter includes field: canonical_metadata" + }, + { + "id": "generic-field-canonical_format", + "status": "pass", + "detail": "generic adapter includes field: canonical_format" + }, + { + "id": "generic-field-activation_mode", + "status": "pass", + "detail": "generic adapter includes field: activation_mode" + }, + { + "id": "generic-field-execution_context", + "status": "pass", + "detail": "generic adapter includes field: execution_context" + }, + { + "id": "generic-field-shell", + "status": "pass", + "detail": "generic adapter includes field: shell" + }, + { + "id": "generic-field-trust_level", + "status": "pass", + "detail": "generic adapter includes field: trust_level" + }, + { + "id": "generic-field-remote_inline_execution", + "status": "pass", + "detail": "generic adapter includes field: remote_inline_execution" + }, + { + "id": "generic-field-degradation_strategy", + "status": "pass", + "detail": "generic adapter includes field: degradation_strategy" + }, + { + "id": "generic-field-portability_profile", + "status": "pass", + "detail": "generic adapter includes field: portability_profile" + }, + { + "id": "vscode-adapter", + "status": "pass", + "detail": "Adapter exists for target: vscode" + }, + { + "id": "vscode-field-name", + "status": "pass", + "detail": "vscode adapter includes field: name" + }, + { + "id": "vscode-field-description", + "status": "pass", + "detail": "vscode adapter includes field: description" + }, + { + "id": "vscode-field-version", + "status": "pass", + "detail": "vscode adapter includes field: version" + }, + { + "id": "vscode-field-display_name", + "status": "pass", + "detail": "vscode adapter includes field: display_name" + }, + { + "id": "vscode-field-short_description", + "status": "pass", + "detail": "vscode adapter includes field: short_description" + }, + { + "id": "vscode-field-default_prompt", + "status": "pass", + "detail": "vscode adapter includes field: default_prompt" + }, + { + "id": "vscode-field-job_to_be_done", + "status": "pass", + "detail": "vscode adapter includes field: job_to_be_done" + }, + { + "id": "vscode-field-ir_source", + "status": "pass", + "detail": "vscode adapter includes field: ir_source" + }, + { + "id": "vscode-field-ir_schema_version", + "status": "pass", + "detail": "vscode adapter includes field: ir_schema_version" + }, + { + "id": "vscode-field-semantic_contract", + "status": "pass", + "detail": "vscode adapter includes field: semantic_contract" + }, + { + "id": "vscode-field-semantic_parity", + "status": "pass", + "detail": "vscode adapter includes field: semantic_parity" + }, + { + "id": "vscode-field-compiler", + "status": "pass", + "detail": "vscode adapter includes field: compiler" + }, + { + "id": "vscode-field-compiled_contract", + "status": "pass", + "detail": "vscode adapter includes field: compiled_contract" + }, + { + "id": "vscode-field-permission_contract", + "status": "pass", + "detail": "vscode adapter includes field: permission_contract" + }, + { + "id": "vscode-field-target_permission_contract", + "status": "pass", + "detail": "vscode adapter includes field: target_permission_contract" + }, + { + "id": "vscode-field-target_native_contract", + "status": "pass", + "detail": "vscode adapter includes field: target_native_contract" + }, + { + "id": "vscode-field-target_transform", + "status": "pass", + "detail": "vscode adapter includes field: target_transform" + }, + { + "id": "vscode-field-canonical_metadata", + "status": "pass", + "detail": "vscode adapter includes field: canonical_metadata" + }, + { + "id": "vscode-field-canonical_format", + "status": "pass", + "detail": "vscode adapter includes field: canonical_format" + }, + { + "id": "vscode-field-activation_mode", + "status": "pass", + "detail": "vscode adapter includes field: activation_mode" + }, + { + "id": "vscode-field-execution_context", + "status": "pass", + "detail": "vscode adapter includes field: execution_context" + }, + { + "id": "vscode-field-shell", + "status": "pass", + "detail": "vscode adapter includes field: shell" + }, + { + "id": "vscode-field-trust_level", + "status": "pass", + "detail": "vscode adapter includes field: trust_level" + }, + { + "id": "vscode-field-remote_inline_execution", + "status": "pass", + "detail": "vscode adapter includes field: remote_inline_execution" + }, + { + "id": "vscode-field-degradation_strategy", + "status": "pass", + "detail": "vscode adapter includes field: degradation_strategy" + }, + { + "id": "vscode-field-portability_profile", + "status": "pass", + "detail": "vscode adapter includes field: portability_profile" + }, + { + "id": "openai-file-targets/openai/adapter.json", + "status": "pass", + "detail": "Package contains targets/openai/adapter.json" + }, + { + "id": "openai-file-targets/openai/agents/openai.yaml", + "status": "pass", + "detail": "Package contains targets/openai/agents/openai.yaml" + }, + { + "id": "claude-file-targets/claude/adapter.json", + "status": "pass", + "detail": "Package contains targets/claude/adapter.json" + }, + { + "id": "claude-file-targets/claude/README.md", + "status": "pass", + "detail": "Package contains targets/claude/README.md" + }, + { + "id": "generic-file-targets/generic/adapter.json", + "status": "pass", + "detail": "Package contains targets/generic/adapter.json" + }, + { + "id": "vscode-file-targets/vscode/adapter.json", + "status": "pass", + "detail": "Package contains targets/vscode/adapter.json" + }, + { + "id": "vscode-file-targets/vscode/README.md", + "status": "pass", + "detail": "Package contains targets/vscode/README.md" + }, + { + "id": "archive-safe-paths", + "status": "pass", + "detail": "Archive has no absolute or parent-traversal entries" + }, + { + "id": "archive-entry-yao-meta-skill/SKILL.md", + "status": "pass", + "detail": "Archive contains yao-meta-skill/SKILL.md" + }, + { + "id": "archive-entry-yao-meta-skill/manifest.json", + "status": "pass", + "detail": "Archive contains yao-meta-skill/manifest.json" + }, + { + "id": "archive-entry-yao-meta-skill/agents/interface.yaml", + "status": "pass", + "detail": "Archive contains yao-meta-skill/agents/interface.yaml" + }, + { + "id": "archive-single-skill-entrypoint", + "status": "pass", + "detail": "Archive exposes only the root SKILL.md entrypoint" + }, + { + "id": "archive-excludes-generated", + "status": "pass", + "detail": "Archive excludes generated dist/, .previews/, and tests/tmp* contents" + }, + { + "id": "registry-ok", + "status": "pass", + "detail": "Registry audit is OK" + }, + { + "id": "registry-name-match", + "status": "pass", + "detail": "Registry package name matches package manifest" + }, + { + "id": "registry-version-match", + "status": "pass", + "detail": "Registry package version matches package manifest" + }, + { + "id": "registry-compat-openai", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: openai" + }, + { + "id": "registry-compat-claude", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: claude" + }, + { + "id": "registry-compat-generic", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: generic" + }, + { + "id": "registry-compat-vscode", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: vscode" + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "manifest": "dist/manifest.json", + "archive": "dist/yao-meta-skill.zip", + "json": "reports/package_verification.json", + "markdown": "reports/package_verification.md" + } +} diff --git a/reports/package_verification.md b/reports/package_verification.md new file mode 100644 index 0000000..70df964 --- /dev/null +++ b/reports/package_verification.md @@ -0,0 +1,152 @@ +# Package Verification + +- OK: `True` +- Package directory: `dist` +- Targets: `4 / 4` adapters present +- Archive present: `True` +- Archive SHA256: `c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7` +- Nested SKILL.md entries: `0` +- Failures: `0` +- Warnings: `0` + +## Checks + +| Check | Status | Detail | +| --- | --- | --- | +| `package-manifest` | `pass` | Package manifest exists: dist/manifest.json | +| `openai-adapter` | `pass` | Adapter exists for target: openai | +| `openai-field-name` | `pass` | openai adapter includes field: name | +| `openai-field-description` | `pass` | openai adapter includes field: description | +| `openai-field-version` | `pass` | openai adapter includes field: version | +| `openai-field-display_name` | `pass` | openai adapter includes field: display_name | +| `openai-field-short_description` | `pass` | openai adapter includes field: short_description | +| `openai-field-default_prompt` | `pass` | openai adapter includes field: default_prompt | +| `openai-field-job_to_be_done` | `pass` | openai adapter includes field: job_to_be_done | +| `openai-field-ir_source` | `pass` | openai adapter includes field: ir_source | +| `openai-field-ir_schema_version` | `pass` | openai adapter includes field: ir_schema_version | +| `openai-field-semantic_contract` | `pass` | openai adapter includes field: semantic_contract | +| `openai-field-semantic_parity` | `pass` | openai adapter includes field: semantic_parity | +| `openai-field-compiler` | `pass` | openai adapter includes field: compiler | +| `openai-field-compiled_contract` | `pass` | openai adapter includes field: compiled_contract | +| `openai-field-permission_contract` | `pass` | openai adapter includes field: permission_contract | +| `openai-field-target_permission_contract` | `pass` | openai adapter includes field: target_permission_contract | +| `openai-field-target_native_contract` | `pass` | openai adapter includes field: target_native_contract | +| `openai-field-target_transform` | `pass` | openai adapter includes field: target_transform | +| `openai-field-canonical_metadata` | `pass` | openai adapter includes field: canonical_metadata | +| `openai-field-canonical_format` | `pass` | openai adapter includes field: canonical_format | +| `openai-field-activation_mode` | `pass` | openai adapter includes field: activation_mode | +| `openai-field-execution_context` | `pass` | openai adapter includes field: execution_context | +| `openai-field-shell` | `pass` | openai adapter includes field: shell | +| `openai-field-trust_level` | `pass` | openai adapter includes field: trust_level | +| `openai-field-remote_inline_execution` | `pass` | openai adapter includes field: remote_inline_execution | +| `openai-field-degradation_strategy` | `pass` | openai adapter includes field: degradation_strategy | +| `openai-field-portability_profile` | `pass` | openai adapter includes field: portability_profile | +| `claude-adapter` | `pass` | Adapter exists for target: claude | +| `claude-field-name` | `pass` | claude adapter includes field: name | +| `claude-field-description` | `pass` | claude adapter includes field: description | +| `claude-field-version` | `pass` | claude adapter includes field: version | +| `claude-field-display_name` | `pass` | claude adapter includes field: display_name | +| `claude-field-short_description` | `pass` | claude adapter includes field: short_description | +| `claude-field-default_prompt` | `pass` | claude adapter includes field: default_prompt | +| `claude-field-job_to_be_done` | `pass` | claude adapter includes field: job_to_be_done | +| `claude-field-ir_source` | `pass` | claude adapter includes field: ir_source | +| `claude-field-ir_schema_version` | `pass` | claude adapter includes field: ir_schema_version | +| `claude-field-semantic_contract` | `pass` | claude adapter includes field: semantic_contract | +| `claude-field-semantic_parity` | `pass` | claude adapter includes field: semantic_parity | +| `claude-field-compiler` | `pass` | claude adapter includes field: compiler | +| `claude-field-compiled_contract` | `pass` | claude adapter includes field: compiled_contract | +| `claude-field-permission_contract` | `pass` | claude adapter includes field: permission_contract | +| `claude-field-target_permission_contract` | `pass` | claude adapter includes field: target_permission_contract | +| `claude-field-target_native_contract` | `pass` | claude adapter includes field: target_native_contract | +| `claude-field-target_transform` | `pass` | claude adapter includes field: target_transform | +| `claude-field-canonical_metadata` | `pass` | claude adapter includes field: canonical_metadata | +| `claude-field-canonical_format` | `pass` | claude adapter includes field: canonical_format | +| `claude-field-activation_mode` | `pass` | claude adapter includes field: activation_mode | +| `claude-field-execution_context` | `pass` | claude adapter includes field: execution_context | +| `claude-field-shell` | `pass` | claude adapter includes field: shell | +| `claude-field-trust_level` | `pass` | claude adapter includes field: trust_level | +| `claude-field-remote_inline_execution` | `pass` | claude adapter includes field: remote_inline_execution | +| `claude-field-degradation_strategy` | `pass` | claude adapter includes field: degradation_strategy | +| `claude-field-portability_profile` | `pass` | claude adapter includes field: portability_profile | +| `generic-adapter` | `pass` | Adapter exists for target: generic | +| `generic-field-name` | `pass` | generic adapter includes field: name | +| `generic-field-description` | `pass` | generic adapter includes field: description | +| `generic-field-version` | `pass` | generic adapter includes field: version | +| `generic-field-display_name` | `pass` | generic adapter includes field: display_name | +| `generic-field-short_description` | `pass` | generic adapter includes field: short_description | +| `generic-field-default_prompt` | `pass` | generic adapter includes field: default_prompt | +| `generic-field-job_to_be_done` | `pass` | generic adapter includes field: job_to_be_done | +| `generic-field-ir_source` | `pass` | generic adapter includes field: ir_source | +| `generic-field-ir_schema_version` | `pass` | generic adapter includes field: ir_schema_version | +| `generic-field-semantic_contract` | `pass` | generic adapter includes field: semantic_contract | +| `generic-field-semantic_parity` | `pass` | generic adapter includes field: semantic_parity | +| `generic-field-compiler` | `pass` | generic adapter includes field: compiler | +| `generic-field-compiled_contract` | `pass` | generic adapter includes field: compiled_contract | +| `generic-field-permission_contract` | `pass` | generic adapter includes field: permission_contract | +| `generic-field-target_permission_contract` | `pass` | generic adapter includes field: target_permission_contract | +| `generic-field-target_native_contract` | `pass` | generic adapter includes field: target_native_contract | +| `generic-field-target_transform` | `pass` | generic adapter includes field: target_transform | +| `generic-field-canonical_metadata` | `pass` | generic adapter includes field: canonical_metadata | +| `generic-field-canonical_format` | `pass` | generic adapter includes field: canonical_format | +| `generic-field-activation_mode` | `pass` | generic adapter includes field: activation_mode | +| `generic-field-execution_context` | `pass` | generic adapter includes field: execution_context | +| `generic-field-shell` | `pass` | generic adapter includes field: shell | +| `generic-field-trust_level` | `pass` | generic adapter includes field: trust_level | +| `generic-field-remote_inline_execution` | `pass` | generic adapter includes field: remote_inline_execution | +| `generic-field-degradation_strategy` | `pass` | generic adapter includes field: degradation_strategy | +| `generic-field-portability_profile` | `pass` | generic adapter includes field: portability_profile | +| `vscode-adapter` | `pass` | Adapter exists for target: vscode | +| `vscode-field-name` | `pass` | vscode adapter includes field: name | +| `vscode-field-description` | `pass` | vscode adapter includes field: description | +| `vscode-field-version` | `pass` | vscode adapter includes field: version | +| `vscode-field-display_name` | `pass` | vscode adapter includes field: display_name | +| `vscode-field-short_description` | `pass` | vscode adapter includes field: short_description | +| `vscode-field-default_prompt` | `pass` | vscode adapter includes field: default_prompt | +| `vscode-field-job_to_be_done` | `pass` | vscode adapter includes field: job_to_be_done | +| `vscode-field-ir_source` | `pass` | vscode adapter includes field: ir_source | +| `vscode-field-ir_schema_version` | `pass` | vscode adapter includes field: ir_schema_version | +| `vscode-field-semantic_contract` | `pass` | vscode adapter includes field: semantic_contract | +| `vscode-field-semantic_parity` | `pass` | vscode adapter includes field: semantic_parity | +| `vscode-field-compiler` | `pass` | vscode adapter includes field: compiler | +| `vscode-field-compiled_contract` | `pass` | vscode adapter includes field: compiled_contract | +| `vscode-field-permission_contract` | `pass` | vscode adapter includes field: permission_contract | +| `vscode-field-target_permission_contract` | `pass` | vscode adapter includes field: target_permission_contract | +| `vscode-field-target_native_contract` | `pass` | vscode adapter includes field: target_native_contract | +| `vscode-field-target_transform` | `pass` | vscode adapter includes field: target_transform | +| `vscode-field-canonical_metadata` | `pass` | vscode adapter includes field: canonical_metadata | +| `vscode-field-canonical_format` | `pass` | vscode adapter includes field: canonical_format | +| `vscode-field-activation_mode` | `pass` | vscode adapter includes field: activation_mode | +| `vscode-field-execution_context` | `pass` | vscode adapter includes field: execution_context | +| `vscode-field-shell` | `pass` | vscode adapter includes field: shell | +| `vscode-field-trust_level` | `pass` | vscode adapter includes field: trust_level | +| `vscode-field-remote_inline_execution` | `pass` | vscode adapter includes field: remote_inline_execution | +| `vscode-field-degradation_strategy` | `pass` | vscode adapter includes field: degradation_strategy | +| `vscode-field-portability_profile` | `pass` | vscode adapter includes field: portability_profile | +| `openai-file-targets/openai/adapter.json` | `pass` | Package contains targets/openai/adapter.json | +| `openai-file-targets/openai/agents/openai.yaml` | `pass` | Package contains targets/openai/agents/openai.yaml | +| `claude-file-targets/claude/adapter.json` | `pass` | Package contains targets/claude/adapter.json | +| `claude-file-targets/claude/README.md` | `pass` | Package contains targets/claude/README.md | +| `generic-file-targets/generic/adapter.json` | `pass` | Package contains targets/generic/adapter.json | +| `vscode-file-targets/vscode/adapter.json` | `pass` | Package contains targets/vscode/adapter.json | +| `vscode-file-targets/vscode/README.md` | `pass` | Package contains targets/vscode/README.md | +| `archive-safe-paths` | `pass` | Archive has no absolute or parent-traversal entries | +| `archive-entry-yao-meta-skill/SKILL.md` | `pass` | Archive contains yao-meta-skill/SKILL.md | +| `archive-entry-yao-meta-skill/manifest.json` | `pass` | Archive contains yao-meta-skill/manifest.json | +| `archive-entry-yao-meta-skill/agents/interface.yaml` | `pass` | Archive contains yao-meta-skill/agents/interface.yaml | +| `archive-single-skill-entrypoint` | `pass` | Archive exposes only the root SKILL.md entrypoint | +| `archive-excludes-generated` | `pass` | Archive excludes generated dist/, .previews/, and tests/tmp* contents | +| `registry-ok` | `pass` | Registry audit is OK | +| `registry-name-match` | `pass` | Registry package name matches package manifest | +| `registry-version-match` | `pass` | Registry package version matches package manifest | +| `registry-compat-openai` | `pass` | Registry compatibility is reviewable for target: openai | +| `registry-compat-claude` | `pass` | Registry compatibility is reviewable for target: claude | +| `registry-compat-generic` | `pass` | Registry compatibility is reviewable for target: generic | +| `registry-compat-vscode` | `pass` | Registry compatibility is reviewable for target: vscode | + +## Failures + +- None + +## Warnings + +- None diff --git a/reports/portability_score.json b/reports/portability_score.json new file mode 100644 index 0000000..a6c25b6 --- /dev/null +++ b/reports/portability_score.json @@ -0,0 +1,27 @@ +{ + "ok": true, + "score": 100, + "band": "world-class", + "summary": { + "adapter_target_count": 4, + "activation_mode": "manual", + "execution_context": "inline", + "shell": "bash", + "trust_level": "local", + "remote_inline_execution": "forbid", + "degradation_coverage": 4, + "snapshot_count": 4, + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "breakdown": { + "platform_neutral_semantic_source": 15, + "adapter_target_coverage": 15, + "activation_metadata": 10, + "execution_metadata": 10, + "trust_boundary_metadata": 15, + "degradation_strategy_coverage": 10, + "contract_and_expectation_coverage": 15, + "consumer_validation_and_snapshots": 10 + } +} diff --git a/reports/portability_score.md b/reports/portability_score.md new file mode 100644 index 0000000..8ed9cab --- /dev/null +++ b/reports/portability_score.md @@ -0,0 +1,23 @@ +# Portability Score + +- score: `100/100` +- band: `world-class` +- adapter targets: `4` +- activation mode: `manual` +- execution context: `inline` +- shell: `bash` +- trust level: `local` +- remote inline execution: `forbid` +- Skill IR source: `skill-ir/examples/yao-meta-skill.json` +- Skill IR schema: `2.0.0` + +| Component | Score | +| --- | ---: | +| `platform_neutral_semantic_source` | 15 | +| `adapter_target_coverage` | 15 | +| `activation_metadata` | 10 | +| `execution_metadata` | 10 | +| `trust_boundary_metadata` | 15 | +| `degradation_strategy_coverage` | 10 | +| `contract_and_expectation_coverage` | 15 | +| `consumer_validation_and_snapshots` | 10 | diff --git a/reports/promotion_decisions.json b/reports/promotion_decisions.json new file mode 100644 index 0000000..59e6cc3 --- /dev/null +++ b/reports/promotion_decisions.json @@ -0,0 +1,795 @@ +{ + "ok": true, + "summary": { + "promote": 0, + "keep_current": 3, + "blocked": 0 + }, + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "winner_tokens": 54, + "current_tokens": 54, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "blind_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "adversarial_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.644, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "yao-meta-skill", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Create and improve agent skills.", + "tokens": 8, + "dev_errors": 1, + "holdout_errors": 0 + }, + { + "name": "yao-meta-skill", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Minimal", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 41, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 56, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 60, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Trigger when requests mention workflows, prompts, transcripts, or docs and the job is to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 77, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 83, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.089, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.496, + "max_non_trigger_score": 0.211, + "score_gap": 0.285, + "threshold_margin": 0.016, + "positive_threshold_buffer": 0.016, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.805, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "team-frontend-review", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "tokens": 52, + "dev_errors": 3, + "holdout_errors": 0 + }, + { + "name": "team-frontend-review", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 62, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 64, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Trigger when requests mention React components, forms, frontend diffs, or UI code and the job is to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 84, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 90, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 1, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.45, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.037, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.057, + "score_gap": 0.023, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.423, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.82, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.645, + "max_non_trigger_score": 0.228, + "score_gap": 0.417, + "threshold_margin": 0.165, + "positive_threshold_buffer": 0.165, + "negative_threshold_buffer": 0.252, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.907, + "mean_non_trigger_score": 0.083, + "mean_near_neighbor_score": 0.139, + "min_positive_score": 0.815, + "max_non_trigger_score": 0.277, + "score_gap": 0.538, + "threshold_margin": 0.063, + "positive_threshold_buffer": 0.475, + "negative_threshold_buffer": 0.063, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_gap_present", + "current_holdout_risk", + "adversarial_watch_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.709, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "governed-incident-command", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, and stakeholder requirements. Use when asked to standardize incident review, severity assessment, incident communication, or incident packet assembly into one reusable operational skill. Do not use for generic debugging, one-off incident explanations, or brainstorming possible response ideas.", + "tokens": 93, + "dev_errors": 0, + "holdout_errors": 1 + }, + { + "name": "governed-incident-command", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Trigger when requests mention alerts, incident notes, transcripts, or stakeholder requirements and the job is to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 51, + "dev_errors": 1, + "holdout_errors": 1, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + } + ] +} diff --git a/reports/promotion_decisions.md b/reports/promotion_decisions.md new file mode 100644 index 0000000..9be35c2 --- /dev/null +++ b/reports/promotion_decisions.md @@ -0,0 +1,7 @@ +# Promotion Decisions + +| Target | Decision | Winner | Route Clean | Judge Agreement | Adv Risk | Causes | Next Action | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `yao-meta-skill` | `keep_current` | `Current` | True | 1.0 | healthy | no_candidate_outperformed_current | Keep the current description and open a new candidate only when fresh route evidence appears. | +| `team-frontend-review` | `keep_current` | `Current` | True | 1.0 | healthy | no_candidate_outperformed_current, current_holdout_risk | Keep the current description and open a new candidate only when fresh route evidence appears. | +| `governed-incident-command` | `keep_current` | `Current` | True | 1.0 | watch | no_candidate_outperformed_current, current_holdout_gap_present, current_holdout_risk, adversarial_watch_risk | Keep the current description and open a new candidate only when fresh route evidence appears. | diff --git a/reports/prompt-quality-profile.json b/reports/prompt-quality-profile.json new file mode 100644 index 0000000..4ce2fb8 --- /dev/null +++ b/reports/prompt-quality-profile.json @@ -0,0 +1,135 @@ +{ + "skill_name": "yao-meta-skill-trigger-coverage-merge", + "relevance": "prompt-aware", + "primary_task_family": { + "key": "execution_operation", + "label": "Execution operation", + "matched_keywords": [ + "workflow", + "runbook" + ] + }, + "task_families": [ + { + "key": "execution_operation", + "label": "Execution operation", + "score": 2, + "matched_keywords": [ + "workflow", + "runbook" + ], + "role_guidance": "Use an operator role with explicit boundaries, inputs, outputs, and failure handling.", + "task_guidance": "Convert the job into ordered steps with validation checks and stop conditions.", + "format_guidance": "Return a runbook-like handoff with commands, checks, owners, and next actions when relevant." + }, + { + "key": "creative_generation", + "label": "Creative generation", + "score": 1, + "matched_keywords": [ + "content" + ], + "role_guidance": "Use a taste-aware creator role with clear audience, tone, and originality boundaries.", + "task_guidance": "Generate variants, explain selection logic, and preserve the user's distinctive constraints.", + "format_guidance": "Return options with rationale, selection criteria, and refinement paths." + }, + { + "key": "analytical_reasoning", + "label": "Analytical reasoning", + "score": 1, + "matched_keywords": [ + "decision" + ], + "role_guidance": "Use an analyst role that separates evidence, inference, uncertainty, and recommendation.", + "task_guidance": "State assumptions, compare alternatives, and make the decision path inspectable.", + "format_guidance": "Return findings, evidence, tradeoffs, recommendation, and residual risks." + }, + { + "key": "dialogue_interaction", + "label": "Dialogue interaction", + "score": 1, + "matched_keywords": [ + "dialogue" + ], + "role_guidance": "Use a conversational role that asks only high-leverage questions and remembers the user's goal.", + "task_guidance": "Clarify intent, resolve uncertainty, and converge toward a recommendation instead of a long option list.", + "format_guidance": "Return concise prompts, decision points, and reviewer-visible assumptions." + } + ], + "complexity": { + "band": "expert", + "score": 27, + "reason": "multiple task families plus governance, evaluation, or expert-level constraints" + }, + "need_model": { + "explicit_need": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "implicit_need": "The reusable skill needs a stable role, task, and output contract rather than a one-off prompt.", + "scenario": "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions, an existing skill directory that needs refactor, evaluation, packaging, or governance hardening, target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution, benchmark references, local constraints, desired maturity tier, and review standards", + "user_level": "infer from examples and standards; ask only if it changes output depth", + "success_standard": "trigger boundaries must be tested with should-trigger and should-not-trigger cases, production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence, governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation, generated reports should be bilingual or reviewer-friendly when they are user-facing, each new asset must earn its place by reducing ambiguity, risk, or repeated work" + }, + "rtf_to_skill": { + "role": "Use an operator role with explicit boundaries, inputs, outputs, and failure handling.", + "task": "Convert the job into ordered steps with validation checks and stop conditions.", + "format": "Return a runbook-like handoff with commands, checks, owners, and next actions when relevant." + }, + "quality_matrix": [ + { + "key": "completeness", + "label": "Completeness", + "score": 100, + "matched_signals": [ + "input", + "output", + "constraint", + "standard" + ], + "repair": "Name missing inputs, outputs, constraints, or success standards before deepening the package." + }, + { + "key": "clarity", + "label": "Clarity", + "score": 85, + "matched_signals": [ + "clear" + ], + "repair": "Replace broad verbs with observable actions and define what done means." + }, + { + "key": "consistency", + "label": "Consistency", + "score": 90, + "matched_signals": [ + "aligned", + "boundary" + ], + "repair": "Check that role, task, format, exclusions, and examples do not contradict each other." + }, + { + "key": "practicality", + "label": "Practicality", + "score": 90, + "matched_signals": [ + "use", + "workflow" + ], + "repair": "Add runnable steps, examples, or verification cues instead of abstract advice." + }, + { + "key": "specificity", + "label": "Specificity", + "score": 80, + "matched_signals": [], + "repair": "Anchor wording in the user's audience, domain nouns, and target outcome." + } + ], + "overall_quality_score": 89.0, + "self_repair_checks": [ + "Check explicit need, implicit need, scenario, user level, and success standard before deepening.", + "Map Role, Task, and Format into skill behavior, not decorative prompt labels.", + "Ask one focused clarification only when missing information changes the package boundary.", + "Add tests or examples for prompt-heavy behavior before treating it as reusable.", + "Keep prompt methodology in references and reports instead of bloating SKILL.md." + ], + "reviewer_note": "Use this profile when the package depends on prompt behavior, role design, output contracts, or conversation quality." +} \ No newline at end of file diff --git a/reports/prompt-quality-profile.md b/reports/prompt-quality-profile.md new file mode 100644 index 0000000..f11955b --- /dev/null +++ b/reports/prompt-quality-profile.md @@ -0,0 +1,94 @@ +# Prompt Quality Profile + +Skill: `yao-meta-skill-trigger-coverage-merge` +Relevance: `prompt-aware` +Overall quality score: `89.0/100` + +## Primary Task Family + +**Execution operation** +- Matched keywords: workflow, runbook + +## Complexity + +- Band: `expert` +- Score: `27` +- Reason: multiple task families plus governance, evaluation, or expert-level constraints + +## Need Model + +- Explicit Need: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse. +- Implicit Need: The reusable skill needs a stable role, task, and output contract rather than a one-off prompt. +- Scenario: rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions, an existing skill directory that needs refactor, evaluation, packaging, or governance hardening, target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution, benchmark references, local constraints, desired maturity tier, and review standards +- User Level: infer from examples and standards; ask only if it changes output depth +- Success Standard: trigger boundaries must be tested with should-trigger and should-not-trigger cases, production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence, governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation, generated reports should be bilingual or reviewer-friendly when they are user-facing, each new asset must earn its place by reducing ambiguity, risk, or repeated work + +## RTF To Skill Mapping + +- Role: Use an operator role with explicit boundaries, inputs, outputs, and failure handling. +- Task: Convert the job into ordered steps with validation checks and stop conditions. +- Format: Return a runbook-like handoff with commands, checks, owners, and next actions when relevant. + +## Quality Matrix + +### Completeness — 100/100 +- Matched signals: input, output, constraint, standard +- Repair: Name missing inputs, outputs, constraints, or success standards before deepening the package. + +### Clarity — 85/100 +- Matched signals: clear +- Repair: Replace broad verbs with observable actions and define what done means. + +### Consistency — 90/100 +- Matched signals: aligned, boundary +- Repair: Check that role, task, format, exclusions, and examples do not contradict each other. + +### Practicality — 90/100 +- Matched signals: use, workflow +- Repair: Add runnable steps, examples, or verification cues instead of abstract advice. + +### Specificity — 80/100 +- Matched signals: none +- Repair: Anchor wording in the user's audience, domain nouns, and target outcome. + +## Matched Task Families + +### Execution operation +- Score: `2` +- Keywords: workflow, runbook +- Role: Use an operator role with explicit boundaries, inputs, outputs, and failure handling. +- Task: Convert the job into ordered steps with validation checks and stop conditions. +- Format: Return a runbook-like handoff with commands, checks, owners, and next actions when relevant. + +### Creative generation +- Score: `1` +- Keywords: content +- Role: Use a taste-aware creator role with clear audience, tone, and originality boundaries. +- Task: Generate variants, explain selection logic, and preserve the user's distinctive constraints. +- Format: Return options with rationale, selection criteria, and refinement paths. + +### Analytical reasoning +- Score: `1` +- Keywords: decision +- Role: Use an analyst role that separates evidence, inference, uncertainty, and recommendation. +- Task: State assumptions, compare alternatives, and make the decision path inspectable. +- Format: Return findings, evidence, tradeoffs, recommendation, and residual risks. + +### Dialogue interaction +- Score: `1` +- Keywords: dialogue +- Role: Use a conversational role that asks only high-leverage questions and remembers the user's goal. +- Task: Clarify intent, resolve uncertainty, and converge toward a recommendation instead of a long option list. +- Format: Return concise prompts, decision points, and reviewer-visible assumptions. + +## Self-Repair Checks + +- Check explicit need, implicit need, scenario, user level, and success standard before deepening. +- Map Role, Task, and Format into skill behavior, not decorative prompt labels. +- Ask one focused clarification only when missing information changes the package boundary. +- Add tests or examples for prompt-heavy behavior before treating it as reusable. +- Keep prompt methodology in references and reports instead of bloating SKILL.md. + +## Reviewer Note + +Use this profile when the package depends on prompt behavior, role design, output contracts, or conversation quality. diff --git a/reports/python_compatibility.json b/reports/python_compatibility.json new file mode 100644 index 0000000..a9b4eb8 --- /dev/null +++ b/reports/python_compatibility.json @@ -0,0 +1,1441 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-13", + "root": ".", + "summary": { + "target_python": "3.11", + "file_count": 235, + "issue_count": 0, + "syntax_error_count": 0, + "fstring_311_violation_count": 0, + "decision": "pass" + }, + "rules": [ + { + "key": "current-python-syntax", + "reason": "Every scanned Python source file must compile under the running interpreter." + }, + { + "key": "fstring-expression-backslash", + "reason": "Python 3.11 rejects backslashes inside f-string expressions; keep escaping outside the expression." + } + ], + "files": [ + { + "path": "examples/complex-release-orchestrator/generated-skill/scripts/build_release_packet.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "examples/governed-incident-command/generated-skill/scripts/build_incident_packet.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "reports/blind-human-review-2026-06-29/generate_pack.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/adaptation_patch_safety.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/adaptation_report_contracts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/adjudicate_output_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/apply_adaptation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/benchmark_release_lock.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_confusion_matrix.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_skill_atlas.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_skill_atlas_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_skill_atlas_opportunities.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/check_update.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/ci_test.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/collect_feedback.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/compile_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/compile_skill_targets.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/context_sizer.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/create_iteration_snapshot.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/cross_packager.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/cross_packager_contracts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/description_optimizer_reporting.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/diff_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/emit_telemetry_event.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_artifact_roles.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_core.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_phase_queue.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_release.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_skill_os2_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/export_skill_ir.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/github_benchmark_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/governance_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/html_rendering.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/import_output_review_decisions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/import_telemetry_events.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/init_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/judge_blind_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/lint_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/local_output_eval_runner.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/optimize_description.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/output_review_privacy.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/prepare_output_review_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/prepare_world_class_submission_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/probe_runtime_permissions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/promotion_checker.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/propose_adaptation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/provider_output_eval_runner.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/python_compat_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/reference_synthesis_markdown.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/registry_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_adoption_drift_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_architecture_maintainability.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_artifact_design_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_baseline_compare.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_context_reports.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_daily_skillops_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_description_drift_history.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_eval_dashboard.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_evidence_consistency.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_intent_confidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_intent_dialogue.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_iteration_directions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_iteration_ledger.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_output_risk_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_portability_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_prompt_quality_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_reference_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_reference_synthesis.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_regression_history.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_annotations.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_studio.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_viewer.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_waivers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_interpretation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_os2_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_os2_coverage.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_overview.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_social_preview.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_system_model.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_telemetry_hook_recipes.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_weekly_curator_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_claim_guard.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_evidence_intake.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_evidence_ledger.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_evidence_plan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_preflight.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_submission_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/resource_boundary_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_action_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_actions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_data.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_formatting.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_gate_contract.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_gate_helpers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_gates.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_output_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_panels.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_skillops.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_waivers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_viewer_data.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_conformance_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_description_optimization_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_eval_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_output_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_output_execution.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/simulate_install.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_ir_paths.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_os2_coverage_markdown.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_charts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_i18n.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_metrics.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_model.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_sections.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_sources.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skillops_opportunity.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/summarize_user_signals.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/sync_local_install.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/telemetry_native_host.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/trigger_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/trust_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/trust_check_scripts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/upgrade_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/validate_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/verify_package.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_evidence_contract.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_human_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_native_permission_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_native_telemetry_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_operator_runbook_coordination.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_phase_queue.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_preflight_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_preflight_markdown.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_provider_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_repair_checklist.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_source_checks.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_submission_kit_rendering.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_submission_matrix.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_adaptation_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_config.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_create_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_distribution_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_operator_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_output_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser_operations.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser_operator.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_report_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_runtime.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_telemetry.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/review_studio_helpers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/review_studio_world_class_assertions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_adaptation_safety.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_adapter_snapshots.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_adoption_drift.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_architecture_maintainability.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_artifact_design_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_baseline_compare.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_benchmark_reproducibility.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_compile_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_conformance_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_daily_skillops.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_description_optimization.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_evidence_consistency.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_failure_regressions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_feedback.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_github_benchmark_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_html_rendering.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_install_simulation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_intent_confidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_iteration_directions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_local_install_sync.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_operator_ux.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_eval_lab.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_execution_runs.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_review_adjudication.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_review_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_risk_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_package_verification.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_packager_failures.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_portability_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_promotion_checker.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_prompt_quality_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_python_compat_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_quality_checks.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_reference_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_reference_synthesis.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_registry_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_annotations.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_studio.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_viewer.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_waivers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_route_confusion.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_runtime_permission_probes.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_security_boundaries.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_atlas.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_interpretation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_ir.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_ir_paths.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_os2_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_os2_coverage.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_overview.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_report_charts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_report_metrics.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skillops_opportunity.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_system_model.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_emit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_hooks.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_import.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_native_host.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_trust_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_upgrade_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_weekly_curator.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_claim_guard.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_evidence_intake.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_evidence_ledger.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_evidence_plan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_operator_runbook.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_preflight.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_submission_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_submission_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_yao_cli.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_yao_cli_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/world_class_human_fixtures.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/world_class_native_telemetry_fixtures.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/yao_cli_helpers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/yao_cli_report_refresh.py", + "ok": true, + "issue_count": 0, + "issues": [] + } + ], + "issues": [], + "artifacts": { + "json": "reports/python_compatibility.json", + "markdown": "reports/python_compatibility.md" + } +} diff --git a/reports/python_compatibility.md b/reports/python_compatibility.md new file mode 100644 index 0000000..23a3e88 --- /dev/null +++ b/reports/python_compatibility.md @@ -0,0 +1,20 @@ +# Python Compatibility + +Generated at: `2026-06-13` + +## Summary + +- decision: `pass` +- target python: `3.11` +- files scanned: `235` +- issues: `0` +- syntax errors: `0` +- f-string 3.11 violations: `0` + +This report catches Python syntax and compatibility hazards that can pass on a newer local interpreter but fail in the supported CI/runtime interpreter. + +## Issues + +| Path | Line | Rule | Message | +| --- | ---: | --- | --- | +| `none` | 0 | `none` | none | diff --git a/reports/reference-scan.json b/reports/reference-scan.json new file mode 100644 index 0000000..93bdcd7 --- /dev/null +++ b/reports/reference-scan.json @@ -0,0 +1,40 @@ +{ + "skill_name": "yao-meta-skill", + "title": "Yao Meta Skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "scan_focus": [ + { + "label": "Evaluation pattern", + "reason": "This skill already carries eval assets, so benchmark how top examples define trigger boundaries and quality gates." + }, + { + "label": "Execution pattern", + "reason": "There is deterministic logic in scripts, so compare how strong references separate prose from executable steps." + }, + { + "label": "Portability pattern", + "reason": "The package carries neutral metadata, so scan how good references preserve semantics across targets without forking source." + }, + { + "label": "Method pattern", + "reason": "Use the core job description as the anchor for comparison: Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse." + } + ], + "external_references": [], + "user_references": [], + "local_constraints": [], + "borrow_plan": [ + "External benchmark first: let high-quality public references define the upper bound for method, structure, execution, or portability.", + "User references second: use them to understand taste, standards, and directional preferences without copying source phrasing.", + "Local fit third: use local assets only to detect naming conflicts, private dependencies, or compatibility constraints.", + "Borrow patterns, not prose: extract loops, boundaries, metadata, and operator flow without copying source-specific language.", + "Keep the package light: reject any borrowed pattern that increases context cost faster than it increases reliability." + ], + "priority_rule": "External benchmark objects set the pattern ceiling. User references refine taste and standards. Local files only calibrate fit, risk, and compatibility.", + "non_goals": [ + "Do not copy source prose or branding into the new skill.", + "Do not import gates that cost more context than they save.", + "Do not use benchmark scanning to justify scope creep.", + "Do not let local historical habits outrank stronger public benchmarks." + ] +} \ No newline at end of file diff --git a/reports/reference-scan.md b/reports/reference-scan.md new file mode 100644 index 0000000..f5148a3 --- /dev/null +++ b/reports/reference-scan.md @@ -0,0 +1,55 @@ +# Reference Scan + +Skill: `yao-meta-skill` + +## Why This Step Exists + +Use a short benchmark pass before authoring the package in depth. External benchmark objects should define the pattern ceiling. Local files are used afterward only to calibrate fit, privacy, naming, and compatibility. + +## Current Skill Anchor + +- Title: `Yao Meta Skill` +- Description: Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse. + +## Scan Focus + +- **Evaluation pattern**: This skill already carries eval assets, so benchmark how top examples define trigger boundaries and quality gates. +- **Execution pattern**: There is deterministic logic in scripts, so compare how strong references separate prose from executable steps. +- **Portability pattern**: The package carries neutral metadata, so scan how good references preserve semantics across targets without forking source. +- **Method pattern**: Use the core job description as the anchor for comparison: Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse. + +## Priority Rule + +- External benchmark objects set the pattern ceiling. User references refine taste and standards. Local files only calibrate fit, risk, and compatibility. + +## External Benchmark Objects + +- No explicit external benchmark objects recorded yet. +- Recommended: capture 2 to 5 external references at most. +- Suggested mix: one method reference, one structure reference, one execution or portability reference. + +## User-Supplied References + +- No user-supplied references recorded yet. +- Ask whether the user has a repo, product, page, workflow, or prompt example worth learning from. +- Treat these as pattern references only, not as text to be copied. + +## Local Fit Check + +- No local constraints recorded yet. +- Use this section for naming collisions, private dependencies, compatibility limits, or existing library conventions. + +## Borrow Plan + +- External benchmark first: let high-quality public references define the upper bound for method, structure, execution, or portability. +- User references second: use them to understand taste, standards, and directional preferences without copying source phrasing. +- Local fit third: use local assets only to detect naming conflicts, private dependencies, or compatibility constraints. +- Borrow patterns, not prose: extract loops, boundaries, metadata, and operator flow without copying source-specific language. +- Keep the package light: reject any borrowed pattern that increases context cost faster than it increases reliability. + +## Non-Goals + +- Do not copy source prose or branding into the new skill. +- Do not import gates that cost more context than they save. +- Do not use benchmark scanning to justify scope creep. +- Do not let local historical habits outrank stronger public benchmarks. diff --git a/reports/reference-synthesis.json b/reports/reference-synthesis.json new file mode 100644 index 0000000..1bef3f5 --- /dev/null +++ b/reports/reference-synthesis.json @@ -0,0 +1,265 @@ +{ + "skill_name": "yao-meta-skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "intent_confidence": { + "score": 100, + "band": "high", + "gate_passed": true + }, + "github_benchmarks": [ + { + "name": "obra/superpowers", + "url": "https://github.com/obra/superpowers", + "stars": 226125, + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure." + ] + }, + { + "name": "affaan-m/ECC", + "url": "https://github.com/affaan-m/ECC", + "stars": 214381, + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure." + ] + }, + { + "name": "multica-ai/andrej-karpathy-skills", + "url": "https://github.com/multica-ai/andrej-karpathy-skills", + "stars": 174264, + "borrow": [ + "Borrow explicit validation and quality gates that make iteration safer.", + "Borrow the way it separates explanation, examples, and reusable structure." + ] + } + ], + "source_tracks": [ + { + "source_type": "official", + "name": "Official skill anatomy and context discipline", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "metadata", + "references" + ], + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "why_relevant": "This track matches: metadata, references." + }, + { + "source_type": "research", + "name": "Hypothesis-test-learn loop", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "test", + "iteration" + ], + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "why_relevant": "This track matches: test, iteration." + }, + { + "source_type": "principles", + "name": "Boundary-first design", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "route", + "trigger", + "exclude" + ], + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "why_relevant": "This track matches: route, trigger, exclude." + } + ], + "synthesis": { + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package.", + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure." + ], + "avoid_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier.", + "Do not expand execution assets until route boundaries stay clean.", + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim." + ], + "quality_risers": [ + "Use GitHub repositories for concrete package and workflow patterns.", + "Use curated official or commercial tracks for entrypoint and operator ergonomics.", + "Use research tracks to justify the smallest evaluation loop that still catches regressions.", + "Use principle tracks to keep the package small, boundary-aware, and outcome-driven." + ], + "pattern_gate": { + "threshold": 4, + "source_count": 6, + "accepted": [ + { + "name": "Official skill anatomy and context discipline", + "source_type": "official", + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "Hypothesis-test-learn loop", + "source_type": "research", + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "Boundary-first design", + "source_type": "principles", + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "obra/superpowers", + "source_type": "github", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.; Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "affaan-m/ECC", + "source_type": "github", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.; Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + } + ], + "deferred": [ + { + "name": "multica-ai/andrej-karpathy-skills", + "source_type": "github", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.; Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "gates": { + "recurrence": true, + "generativity": false, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "distinctiveness", + "boundary" + ], + "missing": [ + "generativity" + ], + "score": 3 + } + ], + "summary": "5 accepted, 1 deferred using threshold 4/4." + }, + "conflicts": [ + { + "key": "lightweight_vs_governance", + "summary": "The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_preference": "lightweight or speed-first", + "benchmark_pressure": "governance or evaluation-heavy patterns" + } + ], + "recommendation": { + "summary": "Start by borrowing this pattern: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. Avoid this for the first pass: Do not let packaging or platform concerns swallow the core job boundary.", + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed." + ], + "avoid_for_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier." + ], + "why": "There is a real design conflict to resolve: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_decision_required": true + }, + "visibility": { + "mode": "explicit", + "user_decision_required": true, + "reasons": [ + "design_conflict" + ], + "user_note": "Surface the recommendation because intent is still settling or there is a real design conflict that needs a user call.", + "reviewer_note": "Keep the full benchmark and synthesis evidence visible for authors and reviewers." + }, + "decision_prompt": "Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call.", + "source_mix": { + "github_benchmarks": 3, + "curated_tracks": 3, + "user_references": 0 + } + } +} diff --git a/reports/reference-synthesis.md b/reports/reference-synthesis.md new file mode 100644 index 0000000..a839f84 --- /dev/null +++ b/reports/reference-synthesis.md @@ -0,0 +1,105 @@ +# Reference Synthesis + +Skill: `yao-meta-skill` +- Description: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs. +- Intent confidence: `100/100` (`high`) + +## Live GitHub Benchmarks + +### obra/superpowers +- URL: https://github.com/obra/superpowers +- Stars: `226125` +- Borrow: Borrow the way it turns a messy workflow into a repeatable operating path. +- Borrow: Borrow the clear execution entrypoints and command structure. + +### affaan-m/ECC +- URL: https://github.com/affaan-m/ECC +- Stars: `214381` +- Borrow: Borrow the way it turns a messy workflow into a repeatable operating path. +- Borrow: Borrow the clear execution entrypoints and command structure. + +### multica-ai/andrej-karpathy-skills +- URL: https://github.com/multica-ai/andrej-karpathy-skills +- Stars: `174264` +- Borrow: Borrow explicit validation and quality gates that make iteration safer. +- Borrow: Borrow the way it separates explanation, examples, and reusable structure. + +## Curated World-Class Pattern Tracks + +### Official skill anatomy and context discipline +- Type: `official` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: metadata, references. +- Borrow: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. +- Avoid: Do not let packaging or platform concerns swallow the core job boundary. + +### Hypothesis-test-learn loop +- Type: `research` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: test, iteration. +- Borrow: Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. +- Avoid: Do not create experimental overhead that exceeds the skill's real risk tier. + +### Boundary-first design +- Type: `principles` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: route, trigger, exclude. +- Borrow: Borrow the discipline of defining what the skill should not own before growing the package. +- Avoid: Do not expand execution assets until route boundaries stay clean. + +## Borrow Now + +- Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. +- Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. +- Borrow the discipline of defining what the skill should not own before growing the package. +- Borrow the way it turns a messy workflow into a repeatable operating path. +- Borrow the clear execution entrypoints and command structure. + +## Avoid Now + +- Do not let packaging or platform concerns swallow the core job boundary. +- Do not create experimental overhead that exceeds the skill's real risk tier. +- Do not expand execution assets until route boundaries stay clean. +- Do not import process overhead that only exists for that project's scale. +- Do not copy repo-specific commands or environment assumptions verbatim. + +## Pattern Gate + +- Summary: 5 accepted, 1 deferred using threshold 4/4. +- Acceptance threshold: `4/4` +- Accepted patterns: + - **Official skill anatomy and context discipline**: 4/4 (recurrence, generativity, distinctiveness, boundary) + - **Hypothesis-test-learn loop**: 4/4 (recurrence, generativity, distinctiveness, boundary) + - **Boundary-first design**: 4/4 (recurrence, generativity, distinctiveness, boundary) + - **obra/superpowers**: 4/4 (recurrence, generativity, distinctiveness, boundary) + - **affaan-m/ECC**: 4/4 (recurrence, generativity, distinctiveness, boundary) +- Deferred patterns: + - **multica-ai/andrej-karpathy-skills**: missing generativity + +## Default Recommendation + +- Summary: Start by borrowing this pattern: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. Avoid this for the first pass: Do not let packaging or platform concerns swallow the core job boundary. +- Why: There is a real design conflict to resolve: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure. +- User decision required: `True` + +## Visibility Mode + +- Mode: `explicit` +- Reasons: design_conflict +- User note: Surface the recommendation because intent is still settling or there is a real design conflict that needs a user call. +- Reviewer note: Keep the full benchmark and synthesis evidence visible for authors and reviewers. + +## Conflict Check + +- **lightweight_vs_governance**: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure. + +## Quality Lift Thesis + +- Use GitHub repositories for concrete package and workflow patterns. +- Use curated official or commercial tracks for entrypoint and operator ergonomics. +- Use research tracks to justify the smallest evaluation loop that still catches regressions. +- Use principle tracks to keep the package small, boundary-aware, and outcome-driven. + +## Decision Prompt + +Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call. diff --git a/reports/registry_audit.json b/reports/registry_audit.json new file mode 100644 index 0000000..902803e --- /dev/null +++ b/reports/registry_audit.json @@ -0,0 +1,93 @@ +{ + "ok": true, + "schema_version": "2.0", + "skill_dir": ".", + "registry_dir": "registry", + "package": { + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "agent-skills": "pass", + "vscode": "pass", + "generic": "pass", + "agent-skills-compatible": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "package_verification": "reports/package_verification.json", + "install_simulated": true, + "install_simulation": "reports/install_simulation.json" + }, + "generated_at": "2026-06-13" + }, + "index": { + "schema_version": "2.0", + "generated_at": "2026-06-13", + "package_count": 1, + "packages": [ + { + "name": "yao-meta-skill", + "version": "1.1.0", + "maturity": "governed", + "owner": "Yao Team", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "package_metadata": "registry/packages/yao-meta-skill.json", + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "failures": [], + "warnings": [], + "artifacts": { + "index": "registry/index.json", + "package": "registry/packages/yao-meta-skill.json", + "json": "reports/registry_audit.json", + "markdown": "reports/registry_audit.md" + } +} diff --git a/reports/registry_audit.md b/reports/registry_audit.md new file mode 100644 index 0000000..048b3cf --- /dev/null +++ b/reports/registry_audit.md @@ -0,0 +1,37 @@ +# Registry Audit + +- OK: `True` +- Package: `yao-meta-skill` +- Version: `1.1.0` +- Maturity: `governed` +- Owner: `Yao Team` +- License: `MIT` +- Package SHA256: `db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c` +- Archive SHA256: `c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7` +- Install simulated: `True` + +## Compatibility + +| Target | Status | +| --- | --- | +| `openai` | `pass` | +| `claude` | `pass` | +| `agent-skills` | `pass` | +| `vscode` | `pass` | +| `generic` | `pass` | +| `agent-skills-compatible` | `pass` | + +## Failures + +- None + +## Warnings + +- None + +## Artifacts + +- index: `registry/index.json` +- package: `registry/packages/yao-meta-skill.json` +- json: `reports/registry_audit.json` +- markdown: `reports/registry_audit.md` diff --git a/reports/regression_history.md b/reports/regression_history.md new file mode 100644 index 0000000..55d6827 --- /dev/null +++ b/reports/regression_history.md @@ -0,0 +1,9 @@ +# Regression History + +| Date | Label | Cases | Families | FP | FN | Route Accuracy | Governance | Governed Examples | Notes | +| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | +| 2026-03-31 | Context First Pack | 66 | 21 | 0 | 0 | - | 90 | 1 | added tiered context budgets; added unused resource detection and quality density; added root and example context budget reports | +| 2026-03-31 | Family Suite Expansion | 66 | 21 | 0 | 0 | - | - | 0 | expanded the family-based trigger suite; published the homepage eval dashboard | +| 2026-03-31 | Governance Quality Gates | 66 | 21 | 0 | 0 | - | 90 | 0 | added runnable governance checks; added resource-boundary checks; wired both into make test | +| 2026-03-31 | Governed Assets And History | 66 | 21 | 0 | 0 | - | 90 | 1 | added governance maturity scoring; added regression history snapshots and renderer; added governed incident-command example | +| 2026-04-01 | Route Scorecard Foundation | 13 | 13 | 0 | 0 | 1.0 | - | 1 | added route confusion matrix for sibling skills and no-route cases; published route scorecard and iteration ledger foundation | diff --git a/reports/review-studio.html b/reports/review-studio.html new file mode 100644 index 0000000..76d880d --- /dev/null +++ b/reports/review-studio.html @@ -0,0 +1,952 @@ + + + + + + Yao Meta Skill Review Studio 2.0 + + + + +
    +
    +
    Review Studio 2.0
    +

    Yao Meta Skill

    +

    Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.

    +
    + 审查结论 + review + Score 91/100 +
    +
    + +
    +

    核心指标

    +
    Skill IR2.0.0

    5 targets in platform-neutral contract

    Compiler5/5

    target contracts compiled from Skill IR

    Output Delta100.0

    5 cases; 1 file-backed

    Exec Runs10

    command 10; model 10; recorded 0

    Blind A/B5

    review pairs hide baseline vs with-skill labels

    Review Kit0/5

    pending 5; answer key hidden

    Review A/B0/5

    adjudication decisions; pending 5

    Public Claimblocked

    4 blockers; local reproducible true

    Blueprint21/21

    2.0 coverage; extensions partial 0, planned 0; evidence pending 4

    Runtime5/5

    target conformance pass rate

    Perm Probe4/4

    0 native; 4 installer-enforced

    Trust0

    155 scripts scanned; secrets found

    Py Compat0

    235 files scanned for Python 3.11

    Arch Debt0

    719 largest lines; 0 watchlist; 6 early; 71 CLI handlers; 18 in entrypoint

    Atlas5

    11 scanned skills; route collisions

    Driftlow

    1 metadata events; 0 missed triggers

    Daily Ops5

    proposal-review; approval 0; release lock false

    Weekly Queue5

    curator-review; ready 1; top score 88

    Waivers0

    0 gates covered; human risk decisions

    Intake4/4

    0 valid submissions; 0 invalid

    Claim Guard0

    201 public surfaces scanned

    Notes0/0

    0 open blocker annotations

    Registry1.1.0

    5 targets; MIT license

    Archivepass

    580 zip entries; package verification

    Installpass

    4 adapters; 12 permissions enforced; 0 permission failures

    Upgrademinor

    declared minor; 0 breaking changes

    +
    + +
    +

    审查闸门

    +
    通过

    意图画布

    intent confidence 100/100; Intent is clear enough to package the first routeable version.

    reports/intent-confidence.json 证据
    通过

    触发实验

    13 trigger cases; 0 misroutes; 0 ambiguous

    reports/route_scorecard.json 证据
    关注

    输出实验

    5/5 cases; with-skill 100.0; baseline 0.0; file-backed 1; near-neighbor 1; blind A/B 5; exec 10; command 10; model 10; recorded 0; reviewed 0/5; review pending 5

    reports/output_quality_scorecard.json 证据
    通过

    上下文

    initial load 987/1000; deferred 531767/120000; top deferred scripts 469568; resource governance governed; quality density 141.8

    reports/context_budget.json 证据
    通过

    运行矩阵

    5 / 5 targets pass

    reports/conformance_matrix.json 证据
    通过

    信任报告

    0 secrets; 155 scripts; 3 network-capable scripts; 0 help smoke failures

    reports/security_trust_report.json 证据
    通过

    Python 兼容

    Python 3.11; 235 files; 0 compatibility issues; 0 syntax; 0 f-string 3.11 hazards

    reports/python_compatibility.json 证据
    通过

    架构维护

    232 Python files; 0 hotspots; 0 watchlist files; 6 early watch files; 0 blockers; largest 719 lines; 71 CLI handlers; 18 in entrypoint

    reports/architecture_maintainability.json 证据
    通过

    权限批准

    3/3 permissions approved; gaps 0; required file_write, network, subprocess

    reports/security_trust_report.json + security/permission_policy.json 证据
    通过

    权限探针

    4/4 targets probed; native 0; metadata fallback 4; installer 4; residual risks 4

    reports/runtime_permission_probes.json 证据
    通过

    组合治理

    11 skills, 1 actionable; 0 actionable route collisions; 0 actionable owner gaps; 0 actionable stale; 0 actionable drift; 22 scoped non-actionable issues

    reports/skill_atlas.json 证据
    通过

    运营回路

    1 metadata events; adoption 100.0; missed 0; bad-output 0; risk low; daily proposals 5; daily decision proposal-review; daily release lock false; weekly queue 5 unique; weekly ready 1; weekly top 88; weekly release lock false

    reports/adoption_drift_report.json + reports/skillops/daily + reports/skillops/weekly 证据
    关注

    人工批准

    0 active waivers; 1 warning gates still need reviewer decision

    reports/review_waivers.json 证据
    关注

    世界证据

    4 pending world-class evidence entries; 1 human pending; 3 external pending; source checks 12/19 pass; 7 blocked; overclaim guard true

    reports/world_class_evidence_ledger.json 证据
    通过

    注册审计

    yao-meta-skill 1.1.0; 6/6 compatibility entries pass; install pass with 4 adapters; installer permissions 12 enforced / 0 failures

    reports/registry_audit.json + reports/install_simulation.json 证据
    通过

    发布路线

    0 promote; 3 keep current; 0 blocked; upgrade minor declared / minor recommended

    reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md 证据
    +
    + +
    +

    闸门契约

    +
    Ok
    期望 Gate
    16
    实际 Gate
    16
    缺失 Gate
    未知 Gate
    重复 Gate
    未加权 Gate
    +
    + +
    +

    阻断事项

    无。

    +

    关注事项

    • 输出实验5/5 cases; with-skill 100.0; baseline 0.0; file-backed 1; near-neighbor 1; blind A/B 5; exec 10; command 10; model 10; recorded 0; reviewed 0/5; review pending 5
    • 人工批准0 active waivers; 1 warning gates still need reviewer decision
    • 世界证据4 pending world-class evidence entries; 1 human pending; 3 external pending; source checks 12/19 pass; 7 blocked; overclaim guard true
    +
    + +
    +

    修复动作

    +
    关注

    输出实验

    补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。同步补足盲审声明。

    没有输出质量和人工盲评证据时,Skill 只能证明会触发,不能证明输出真的更好且经得起审查。
    修复位置
    evals/output/cases.jsonl + reports/output_quality_scorecard.md + reports/output_review_kit.html + reports/output_review_adjudication.md + reports/output_review_decisions.json
    验证命令
    python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review
    关注

    人工批准

    对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。

    warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。
    修复位置
    reports/review_waivers.md
    验证命令
    python3 scripts/render_review_waivers.py .
    关注

    世界证据

    补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据,或明确本次发布不声明 world-class 完成。

    世界级结论必须来自已接受的外部/人工证据;计划、metadata fallback、待评审和本地命令都不能替代完成证据。
    修复位置
    reports/world_class_operator_runbook.html + reports/world_class_evidence_ledger.md + reports/world_class_evidence_intake.md + reports/world_class_submission_review.md
    验证命令
    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 .

    证据采集

    以下条目仍需真实外部或人工证据;提交文件、校验命令和阻断检查必须同时闭环。

    pending · external

    Provider Holdout

    model-executed 10; token-observed 10

    提交
    evidence/world_class/submissions/provider-holdout.json
    模板
    evidence/world_class/templates/provider-holdout.intake.json
    阻断
    0 blocked / 3 pass
    修复
    1 repair rows; counts as completion: false
    阶段
    1 blocked phases; counts as completion: false
    下一步
    Run provider-backed holdout cases with real credentials and commit only aggregate evidence.
    阻断检查

    暂无阻断检查。

    阶段队列
    • #20 · unblock-accessUnblock access1 / 1 blockedowners: operator with provider credentialsevidence: provider-holdoutSet one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    修复清单
    • #20 · unblock-access · precheckprovider-api-keyoperator with provider credentialsRequired env_any precheck is missing.Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    操作命令
    • 准备提交python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions
    • 校验入口python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • 审查提交python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • 刷新台账python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    首要步骤
    1. Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    2. For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60
    3. For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120
    采集契约
    来源要求
    • provider-backed model run
    • observed timing
    • observed token metadata
    通过条件
    • reports/output_execution_runs.json summary.model_executed_count > 0
    • reports/output_execution_runs.json summary.timing_observed_count > 0
    • reports/output_execution_runs.json summary.token_observed_count > 0
    • reports/skill_os2_audit.json item provider-holdout status becomes pass
    证据资产
    • reports/output_execution_runs.json
    • reports/output_execution_runs.md
    • reports/skill_os2_audit.json
    • evidence/world_class/intake.schema.json
    • evidence/world_class/templates/provider-holdout.intake.json
    资产角色

    source: world-class-submission-kit; counts as evidence: false; prefill counts as evidence: false

    • submission-ref1 / 1 readyartifact_refs: trueRows marked submission-ref are the aggregate paths expected in artifact_refs.
    • supporting-evidence6 / 6 readyartifact_refs: falseSupporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.
    隐私边界
    • Do not commit provider credentials or environment dumps.
    • The output execution report records output hashes and aggregate run metadata, not raw provider prompts.
    pending · human

    Human Adjudication

    0/5 decisions; pending 5

    提交
    evidence/world_class/submissions/human-adjudication.json
    模板
    evidence/world_class/templates/human-adjudication.intake.json
    阻断
    5 blocked / 5 pass
    修复
    6 repair rows; counts as completion: false
    阶段
    2 blocked phases; counts as completion: false
    下一步
    Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication.
    阻断检查
    • No pending decisionspending_count: 5 / ==0Record a reviewer choice and reason for every pair.
    • Judgments completejudgment_count: 0 / ==pair_countEvery pair needs one valid human judgment.
    • Reviewer metadatareviewer_metadata_present: False / trueRecord reviewer and reviewed_at before adjudication can count.
    • Blind review attestedblind_review_attested: False / trueSet reviewer_attestation only after choices are completed before opening the answer key.
    • Human evidence readyready_for_human_evidence: False / trueComplete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.
    阶段队列
    • #20 · unblock-accessUnblock access1 / 1 blockedowners: human reviewerevidence: human-adjudicationAssign a real reviewer identity before claiming human adjudication.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-sourceCollect source5 / 5 blockedowners: human reviewerevidence: human-adjudicationSet reviewer_attestation only after choices are completed before opening the answer key.python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    修复清单
    • #20 · unblock-access · precheckhuman-reviewerhuman reviewerRequired human precheck is human-required.Assign a real reviewer identity before claiming human adjudication.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-source · source-checkblind_review_attestedhuman reviewerCurrent value False does not satisfy 'true'.Set reviewer_attestation only after choices are completed before opening the answer key.python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-source · source-checkjudgment_counthuman reviewerCurrent value 0 does not satisfy '==pair_count'.Every pair needs one valid human judgment.python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-source · source-checkpending_counthuman reviewerCurrent value 5 does not satisfy '==0'.Record a reviewer choice and reason for every pair.python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-source · source-checkready_for_human_evidencehuman reviewerCurrent value False does not satisfy 'true'.Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-source · source-checkreviewer_metadata_presenthuman reviewerCurrent value False does not satisfy 'true'.Record reviewer and reviewed_at before adjudication can count.python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    操作命令
    • 准备提交python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions
    • 校验入口python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • 审查提交python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • 刷新台账python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    首要步骤
    1. python3 scripts/yao.py output-review-kit --write-template
    2. Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.
    3. python3 scripts/adjudicate_output_review.py --write-template
    采集契约
    来源要求
    • real reviewer identity
    • blind A/B decisions
    • answer key unopened until decisions exist
    通过条件
    • reports/output_review_adjudication.json summary.pending_count == 0
    • reports/output_review_adjudication.json summary.judgment_count == summary.pair_count
    • reports/output_review_adjudication.json summary.invalid_decision_count == 0
    • reports/output_review_adjudication.json summary.reviewer_metadata_present is true
    • reports/output_review_adjudication.json summary.blind_review_attested is true
    • reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json
    • reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text
    • reports/output_review_adjudication.json summary.ready_for_human_evidence is true
    • reports/skill_os2_audit.json item human-adjudication status becomes pass
    证据资产
    • reports/output_blind_review_pack.md
    • reports/output_review_kit.md
    • reports/output_review_decisions.json
    • reports/output_review_adjudication.json
    • reports/output_review_adjudication.md
    资产角色

    source: world-class-submission-kit; counts as evidence: false; prefill counts as evidence: false

    • submission-ref2 / 2 readyartifact_refs: trueRows marked submission-ref are the aggregate paths expected in artifact_refs.
    • supporting-evidence8 / 8 readyartifact_refs: falseSupporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.
    隐私边界
    • Reviewer decisions should not include raw user data or private customer detail.
    • Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.
    • The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.
    • The adjudication evidence stores prompt_sha256 instead of raw prompt text.
    pending · external

    Native Permission Enforcement

    native-enforced targets 0; installer-enforced targets 4

    提交
    evidence/world_class/submissions/native-permission-enforcement.json
    模板
    evidence/world_class/templates/native-permission-enforcement.intake.json
    阻断
    1 blocked / 2 pass
    修复
    2 repair rows; counts as completion: false
    阶段
    2 blocked phases; counts as completion: false
    下一步
    Integrate a real target-client or external installer runtime guard before claiming native permission enforcement.
    阻断检查
    • Native enforcementnative_enforcement_count: 0 / >0Collect real target-client or external runtime guard proof.
    阶段队列
    • #20 · unblock-accessUnblock access1 / 1 blockedowners: target client or installer integratorevidence: native-permission-enforcementAttach a real target-client or external installer runtime guard; metadata fallback is not enough.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-sourceCollect source1 / 1 blockedowners: target client or installer integratorevidence: native-permission-enforcementCollect real target-client or external runtime guard proof.python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    修复清单
    • #20 · unblock-access · prechecknative-guardtarget client or installer integratorRequired external precheck is external-required.Attach a real target-client or external installer runtime guard; metadata fallback is not enough.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-source · source-checknative_enforcement_counttarget client or installer integratorCurrent value 0 does not satisfy '>0'.Collect real target-client or external runtime guard proof.python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    操作命令
    • 准备提交python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions
    • 校验入口python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • 审查提交python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • 刷新台账python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    首要步骤
    1. Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.
    2. Update the generated target adapter only when the guard is actually enforced by that target.
    3. python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip
    采集契约
    来源要求
    • real target client or external installer runtime guard
    • native enforcement flag or externally accepted guard proof
    • residual risk retained for fallback targets
    通过条件
    • reports/runtime_permission_probes.json summary.native_enforcement_count > 0
    • reports/runtime_permission_probes.json summary.failure_count == 0
    • reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence
    • reports/skill_os2_audit.json item native-permission-enforcement status becomes pass
    证据资产
    • dist/targets/*/adapter.json
    • reports/runtime_permission_probes.json
    • reports/runtime_permission_probes.md
    • reports/install_simulation.json
    • reports/install_simulation.md
    资产角色

    source: world-class-submission-kit; counts as evidence: false; prefill counts as evidence: false

    • submission-ref2 / 2 readyartifact_refs: trueRows marked submission-ref are the aggregate paths expected in artifact_refs.
    • supporting-evidence11 / 11 readyartifact_refs: falseSupporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.
    隐私边界
    • Do not mark native_enforcement true for metadata-only fallbacks.
    • Keep residual risks visible for targets that still rely on operator enforcement.
    pending · external

    Native Client Telemetry

    external source events 0; adoption samples 1

    提交
    evidence/world_class/submissions/native-client-telemetry.json
    模板
    evidence/world_class/templates/native-client-telemetry.intake.json
    阻断
    1 blocked / 2 pass
    修复
    2 repair rows; counts as completion: false
    阶段
    2 blocked phases; counts as completion: false
    下一步
    Install a real client against the native host and import production metadata-only events.
    阻断检查
    • External eventsexternal_source_events: 0 / >0Import at least one metadata-only event from a real client.
    阶段队列
    • #20 · unblock-accessUnblock access1 / 1 blockedowners: Browser/Chrome/IDE/provider client integratorevidence: native-client-telemetryInstall a real Browser, Chrome, IDE, or provider client that emits metadata-only events.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-sourceCollect source1 / 1 blockedowners: Browser/Chrome/IDE/provider client integratorevidence: native-client-telemetryImport at least one metadata-only event from a real client.python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    修复清单
    • #20 · unblock-access · precheckexternal-clientBrowser/Chrome/IDE/provider client integratorRequired external precheck is external-required.Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    • #40 · collect-source · source-checkexternal_source_eventsBrowser/Chrome/IDE/provider client integratorCurrent value 0 does not satisfy '>0'.Import at least one metadata-only event from a real client.python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    操作命令
    • 准备提交python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions
    • 校验入口python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • 审查提交python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • 刷新台账python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    首要步骤
    1. python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension://<extension-id>/
    2. Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.
    3. python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl
    采集契约
    来源要求
    • real external client source
    • metadata-only event
    • local-first import path
    通过条件
    • reports/adoption_drift_report.json summary.source_types.external > 0
    • reports/adoption_drift_report.json summary.adoption_sample_count > 0
    • reports/skill_os2_audit.json item native-client-telemetry status becomes pass
    证据资产
    • reports/adoption_drift_report.json
    • reports/adoption_drift_report.md
    • reports/telemetry_hook_recipes.json
    • scripts/telemetry_native_host.py
    • evidence/world_class/intake.schema.json
    资产角色

    source: world-class-submission-kit; counts as evidence: false; prefill counts as evidence: false

    • submission-ref2 / 2 readyartifact_refs: trueRows marked submission-ref are the aggregate paths expected in artifact_refs.
    • supporting-evidence6 / 6 readyartifact_refs: falseSupporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.
    隐私边界
    • Telemetry must remain metadata-only and local-first.
    • Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field.
    +
    + +
    +

    审查批注

    +

    当前批注:0 条批注;开放 0;阻断 0

    +

    当前没有 reviewer 批注。

    +
    + +
    +
    +

    意图画布

    +

    Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse. Primary output: A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.. Exclusions: one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill, general code review or debugging unless the user is packaging that workflow as a skill, raw private material that was not intentionally supplied as skill evidence, platform-specific plugin creation when the request is not about an agent skill package.

    +
    +
    +

    证据路径

    +
    • skill_overviewreports/skill-overview.html
    • review_viewerreports/review-viewer.html
    • output_evalreports/output_quality_scorecard.md
    • output_executionreports/output_execution_runs.md
    • output_blind_reviewreports/output_blind_review_pack.md
    • output_review_kitreports/output_review_kit.md
    • output_review_kit_htmlreports/output_review_kit.html
    • output_review_decisionsreports/output_review_decisions.json
    • output_review_adjudicationreports/output_review_adjudication.md
    • benchmark_reproducibilityreports/benchmark_reproducibility.md
    • skill_os2_coveragereports/skill_os2_coverage.md
    • runtime_conformancereports/conformance_matrix.md
    • trust_reportreports/security_trust_report.md
    • python_compatibilityreports/python_compatibility.md
    • architecture_maintainabilityreports/architecture_maintainability.md
    • permission_policysecurity/permission_policy.md
    • runtime_permissionsreports/runtime_permission_probes.md
    • skill_atlasreports/skill_atlas.html
    • compiled_targetsreports/compiled_targets.md
    • adoption_driftreports/adoption_drift_report.md
    • review_waiversreports/review_waivers.md
    • review_annotationsreports/review_annotations.md
    • adaptation_proposalsreports/adaptation_proposals.md
    • adaptation_approval_ledgerreports/adaptation_approval_ledger.json
    • adaptation_regressionreports/adaptation_regression_report.md
    • world_class_evidence_planreports/world_class_evidence_plan.md
    • world_class_evidence_ledgerreports/world_class_evidence_ledger.md
    • world_class_evidence_intakereports/world_class_evidence_intake.md
    • world_class_evidence_preflightreports/world_class_evidence_preflight.md
    • world_class_evidence_preflight_htmlreports/world_class_evidence_preflight.html
    • world_class_submission_reviewreports/world_class_submission_review.md
    • world_class_operator_runbookreports/world_class_operator_runbook.md
    • world_class_operator_runbook_htmlreports/world_class_operator_runbook.html
    • world_class_claim_guardreports/world_class_claim_guard.md
    • registry_auditreports/registry_audit.md
    • package_verificationreports/package_verification.md
    • install_simulationreports/install_simulation.md
    • upgrade_checkreports/upgrade_check.md
    • migrationdocs/migration-v2.md
    • skill_irskill-ir/examples/yao-meta-skill.json
    • daily_skillopsreports/skillops/daily/2026-07-02.md
    • weekly_curatorreports/skillops/weekly/2026-W27.md
    +
    +
    + +
    +

    触发实验

    13 trigger cases; 0 misroutes; 0 ambiguous

    +

    组合治理

    Skill 数
    11
    纳入审查
    1
    待处理冲突
    0
    待处理 owner
    0
    待处理过期
    0
    Telemetry Report Count
    1
    Actionable Drift Signal Count
    0
    Drift Signal Count
    0
    非行动项
    22
    +
    + +
    +

    输出实验

    案例数
    5
    With Skill
    100
    Baseline
    0
    增益
    100
    Gate
    +

    执行证据

    运行数
    10
    命令执行
    10
    模型执行
    10
    记录样本
    0
    计时样本
    10
    估算 Token
    0
    +
    + +
    +

    盲评包

    Pair Count
    5
    Answer Key Separate
    With Skill Hidden Count
    5
    +

    审定报告

    Pair Count
    5
    Judgment Count
    0
    待审
    5
    Agreement Count
    0
    Disagreement Count
    0
    Invalid Decision Count
    0
    答案揭示
    0
    答案隐藏
    5
    Reviewer Checklist Count
    5
    Reviewer Checklist Pending Count
    5
    +
    + +

    评审清单

    先打开 reports/output_review_kit.md;每张卡片对应一个 blind A/B case,说明当前是否待判、答案是否仍隐藏、应填写的决策文件、有效字段和复跑命令。

    awaiting-decision · 答案隐藏

    skill-package-contract

    Reviewer has not selected A or B yet; answer key remains hidden.

    盲评包
    reports/output_blind_review_pack.json
    决策表
    reports/output_review_decisions.json
    提示哈希
    95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5

    操作命令

    • prepare_review_kitpython3 scripts/yao.py output-review-kit
    • write_templatepython3 scripts/adjudicate_output_review.py --write-template
    • import_decisionspython3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • adjudicatepython3 scripts/yao.py output-review
    • refresh_review_studiopython3 scripts/yao.py review-studio .

    字段要求

    • winner_variantA or B after reading only the blind review pack.
    • confidenceOptional number from 0 to 1.
    • reasonRequired rationale; do not reveal baseline or with-skill labels before adjudication.
    • reviewerHuman reviewer name or review group at the decision-file top level.
    • reviewed_atReview date or timestamp at the decision-file top level.
    • reviewer_attestation.blind_review_completed_before_answer_keyTrue only after the reviewer has completed choices before opening the answer key.
    • reviewer_attestation.answer_key_not_opened_before_decisionsTrue only when the answer key was not opened before decisions were recorded.

    隐私边界

    • Do not paste raw private user data into the decision reason.
    • Do not open the answer key before reviewer choices are recorded.
    • Leave winner_variant blank when the reviewer is not ready to decide.
    awaiting-decision · 答案隐藏

    output-eval-expectation

    Reviewer has not selected A or B yet; answer key remains hidden.

    盲评包
    reports/output_blind_review_pack.json
    决策表
    reports/output_review_decisions.json
    提示哈希
    3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4

    操作命令

    • prepare_review_kitpython3 scripts/yao.py output-review-kit
    • write_templatepython3 scripts/adjudicate_output_review.py --write-template
    • import_decisionspython3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • adjudicatepython3 scripts/yao.py output-review
    • refresh_review_studiopython3 scripts/yao.py review-studio .

    字段要求

    • winner_variantA or B after reading only the blind review pack.
    • confidenceOptional number from 0 to 1.
    • reasonRequired rationale; do not reveal baseline or with-skill labels before adjudication.
    • reviewerHuman reviewer name or review group at the decision-file top level.
    • reviewed_atReview date or timestamp at the decision-file top level.
    • reviewer_attestation.blind_review_completed_before_answer_keyTrue only after the reviewer has completed choices before opening the answer key.
    • reviewer_attestation.answer_key_not_opened_before_decisionsTrue only when the answer key was not opened before decisions were recorded.

    隐私边界

    • Do not paste raw private user data into the decision reason.
    • Do not open the answer key before reviewer choices are recorded.
    • Leave winner_variant blank when the reviewer is not ready to decide.
    awaiting-decision · 答案隐藏

    ir-before-packaging

    Reviewer has not selected A or B yet; answer key remains hidden.

    盲评包
    reports/output_blind_review_pack.json
    决策表
    reports/output_review_decisions.json
    提示哈希
    d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80

    操作命令

    • prepare_review_kitpython3 scripts/yao.py output-review-kit
    • write_templatepython3 scripts/adjudicate_output_review.py --write-template
    • import_decisionspython3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • adjudicatepython3 scripts/yao.py output-review
    • refresh_review_studiopython3 scripts/yao.py review-studio .

    字段要求

    • winner_variantA or B after reading only the blind review pack.
    • confidenceOptional number from 0 to 1.
    • reasonRequired rationale; do not reveal baseline or with-skill labels before adjudication.
    • reviewerHuman reviewer name or review group at the decision-file top level.
    • reviewed_atReview date or timestamp at the decision-file top level.
    • reviewer_attestation.blind_review_completed_before_answer_keyTrue only after the reviewer has completed choices before opening the answer key.
    • reviewer_attestation.answer_key_not_opened_before_decisionsTrue only when the answer key was not opened before decisions were recorded.

    隐私边界

    • Do not paste raw private user data into the decision reason.
    • Do not open the answer key before reviewer choices are recorded.
    • Leave winner_variant blank when the reviewer is not ready to decide.
    awaiting-decision · 答案隐藏

    near-neighbor-boundary

    Reviewer has not selected A or B yet; answer key remains hidden.

    盲评包
    reports/output_blind_review_pack.json
    决策表
    reports/output_review_decisions.json
    提示哈希
    2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0

    操作命令

    • prepare_review_kitpython3 scripts/yao.py output-review-kit
    • write_templatepython3 scripts/adjudicate_output_review.py --write-template
    • import_decisionspython3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • adjudicatepython3 scripts/yao.py output-review
    • refresh_review_studiopython3 scripts/yao.py review-studio .

    字段要求

    • winner_variantA or B after reading only the blind review pack.
    • confidenceOptional number from 0 to 1.
    • reasonRequired rationale; do not reveal baseline or with-skill labels before adjudication.
    • reviewerHuman reviewer name or review group at the decision-file top level.
    • reviewed_atReview date or timestamp at the decision-file top level.
    • reviewer_attestation.blind_review_completed_before_answer_keyTrue only after the reviewer has completed choices before opening the answer key.
    • reviewer_attestation.answer_key_not_opened_before_decisionsTrue only when the answer key was not opened before decisions were recorded.

    隐私边界

    • Do not paste raw private user data into the decision reason.
    • Do not open the answer key before reviewer choices are recorded.
    • Leave winner_variant blank when the reviewer is not ready to decide.
    awaiting-decision · 答案隐藏

    file-backed-governed-package

    Reviewer has not selected A or B yet; answer key remains hidden.

    盲评包
    reports/output_blind_review_pack.json
    决策表
    reports/output_review_decisions.json
    提示哈希
    c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d

    操作命令

    • prepare_review_kitpython3 scripts/yao.py output-review-kit
    • write_templatepython3 scripts/adjudicate_output_review.py --write-template
    • import_decisionspython3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • adjudicatepython3 scripts/yao.py output-review
    • refresh_review_studiopython3 scripts/yao.py review-studio .

    字段要求

    • winner_variantA or B after reading only the blind review pack.
    • confidenceOptional number from 0 to 1.
    • reasonRequired rationale; do not reveal baseline or with-skill labels before adjudication.
    • reviewerHuman reviewer name or review group at the decision-file top level.
    • reviewed_atReview date or timestamp at the decision-file top level.
    • reviewer_attestation.blind_review_completed_before_answer_keyTrue only after the reviewer has completed choices before opening the answer key.
    • reviewer_attestation.answer_key_not_opened_before_decisionsTrue only when the answer key was not opened before decisions were recorded.

    隐私边界

    • Do not paste raw private user data into the decision reason.
    • Do not open the answer key before reviewer choices are recorded.
    • Leave winner_variant blank when the reviewer is not ready to decide.
    +
    +

    发布标准

    Governed 和 Library 至少需要 5 个 output eval cases,并覆盖 file-backed、near-neighbor、boundary case、execution evidence 和 blind A/B review pack。

    +

    人工结论

    没有 reviewer 决策时只显示 pending;只有真实决策文件会进入一致率和分歧统计。

    +
    + +
    +

    评审方式

    先打开 reports/output_review_kit.html 做盲评,填入 reports/output_review_decisions.json,再用 reports/output_review_adjudication.md 核对答案 key。

    +

    运行方式

    reports/output_execution_runs.md 会标明 recorded fixture、command run 或 model run;只有 provider runner 返回 model metadata 时才算 model-executed。

    +
    + +
    +

    运行矩阵

    目标数
    5
    通过数
    5
    Fail Count
    0
    +

    目标编译

    目标数
    5
    通过数
    5
    Warn Count
    0
    Block Count
    0
    失败数
    0
    +
    + +
    +

    上下文

    initial load 987/1000; deferred 531767/120000; top deferred scripts 469568; resource governance governed; quality density 141.8

    +

    编译证据

    Review reports/compiled_targets.md before packaging to inspect target adapter modes, generated files, preserved semantics, warnings, and unsupported features.

    +
    + +
    +

    信任报告

    Secret
    0
    脚本数
    155
    网络脚本
    3
    Help 失败
    0
    包体哈希
    db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c
    +

    安全边界

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

    +
    + +
    +

    Python 兼容

    目标 Python
    3.11
    文件数
    235
    问题数
    0
    语法错误
    0
    F-string 3.11
    0
    +

    解释器边界

    CI 和发布审查以 Python 3.11 兼容为底线;本地更高版本允许的新语法不能绕过兼容门禁。

    +
    + +
    +

    权限批准

    3/3 permissions approved; gaps 0; required file_write, network, subprocess

    +

    批准策略

    高权限能力需要 reviewer、scope、reason、expires_at 和 openai/claude/generic 目标端 enforcement 说明。

    +
    + +
    +

    权限探针

    4/4 targets probed; native 0; metadata fallback 4; installer 4; residual risks 4

    +

    运行边界

    目标数
    4
    通过数
    4
    原生执行
    0
    Metadata fallback
    4
    安装拦截
    4
    安装权限失败
    0
    残余风险
    4
    失败数
    0
    +
    + +
    +

    组合治理

    11 skills, 1 actionable; 0 actionable route collisions; 0 actionable owner gaps; 0 actionable stale; 0 actionable drift; 22 scoped non-actionable issues

    +

    下一动作

    优先处理真实 portfolio 中的 duplicate names、stale skills、owner gaps,再用运营回路判断真实影响。

    +
    + +
    +

    运营回路

    1 metadata events; adoption 100.0; missed 0; bad-output 0; risk low; daily proposals 5; daily decision proposal-review; daily release lock false; weekly queue 5 unique; weekly ready 1; weekly top 88; weekly release lock false

    +

    漂移信号

    事件数
    1
    采用率
    100
    漏触发
    0
    Bad Output Count
    0
    风险带
    low
    +
    + + +
    +

    日常运维

    决策
    proposal-review
    提案数
    5
    Approval Count
    0
    Pending Review Count
    0
    发布锁
    世界级
    Writes Source Files
    Auto Patch Enabled
    +

    周度队列

    决策
    curator-review
    Week Id
    2026-W27
    日报数
    5
    唯一机会
    5
    待批准复核
    1
    提案复核
    4
    Top Score
    88
    发布锁
    Writes Source Files
    Auto Patch Enabled
    +
    + + +
    +

    人工批准

    +

    warning 可以被有边界地接受,但必须写入 reviewer、理由、范围和到期时间;blocker 与 world-class 完成证据不能通过 waiver 变成通过。

    +
    +

    批准概况

    0 active waivers; 1 warning gates still need reviewer decision

    +

    批准台账

    Waiver Count
    0
    Active Count
    0
    Expired Count
    0
    Invalid Count
    0
    覆盖 Gate
    0
    +
    +

    批准候选

    +
    可批准 · needs-reviewer-decision

    Output Lab

    review pending 5; model-executed 10; output failures 0

    Gate
    output-lab
    证据
    reports/output_review_adjudication.md
    选项
    accepted-risk, false-positive, temporary-exception
    边界
    Does not count as provider, human, or public world-class completion evidence.

    审查条件

    • Reviewer confirms this release does not claim provider-backed or human-adjudicated output superiority.
    • Reviewer names the release scope and expiry date.
    • Reviewer links output_review_adjudication or output_execution evidence.
    建议命令python3 scripts/yao.py review-waivers . --add-waiver --gate-key output-lab --reviewer "<reviewer>" --reason "Output Lab has pending human/provider evidence; accepted only for this bounded review scope." --expires-at 2027-06-13 --evidence reports/output_review_adjudication.md
    不可批准 · cannot-waive

    World-Class Evidence

    4 pending evidence entries; 1 human pending; 3 external pending

    Gate
    world-class-evidence
    证据
    reports/world_class_evidence_ledger.md
    选项
    边界
    Non-waivable completion boundary.

    审查条件

    • Do not use a waiver to claim public world-class readiness.
    • Either submit accepted ledger evidence or state that this release does not claim world-class completion.
    • Keep claim guard active until ledger summary.ready_to_claim_world_class is true.
    建议命令python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard .
    +
    + +
    +

    世界证据

    +

    这里列出每个 world-class 证据项的当前状态、完成定义、证据来源、隐私约束和下一步;计划、metadata fallback、待评审和本地命令不会被当成完成证据。

    +
    待补证 · external

    Provider Holdout

    Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.

    负责人
    operator with provider credentials
    当前状态
    model-executed 10; token-observed 10
    下一步
    Run provider-backed holdout cases with real credentials and commit only aggregate evidence.
    观测值
    model_executed_count: 10; timing_observed_count: 10; token_observed_count: 10; accepted: True
    提交态
    status: missing; path: evidence/world_class/submissions/provider-holdout.json; attested_real_evidence: False; privacy_contract_satisfied: False

    执行步骤

    • Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    • For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60
    • For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    完成定义

    • reports/output_execution_runs.json summary.model_executed_count > 0
    • reports/output_execution_runs.json summary.timing_observed_count > 0
    • reports/output_execution_runs.json summary.token_observed_count > 0
    • reports/skill_os2_audit.json item provider-holdout status becomes pass

    证据来源

    • reports/output_execution_runs.json
    • reports/output_execution_runs.md
    • reports/skill_os2_audit.json
    • evidence/world_class/intake.schema.json
    • evidence/world_class/templates/provider-holdout.intake.json
    • reports/world_class_evidence_intake.json
    • reports/world_class_evidence_intake.md

    隐私约束

    • Do not commit provider credentials or environment dumps.
    • The output execution report records output hashes and aggregate run metadata, not raw provider prompts.

    源证据检查

    • Provider model runmodel_executed_count: 10 / >0Run provider-backed output-exec with real credentials.
    • Timing observedtiming_observed_count: 10 / >0Provider execution should record timing metadata.
    • Token usage observedtoken_observed_count: 10 / >0Provider execution should return non-estimated token usage.
    待补证 · human

    Human Adjudication

    Record real blind A/B reviewer decisions before claiming human output review completion.

    负责人
    human reviewer
    当前状态
    0/5 decisions; pending 5
    下一步
    Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication.
    观测值
    pair_count: 5; judgment_count: 0; pending_count: 5; invalid_decision_count: 0; answer_revealed_count: 0; reviewer_metadata_present: False; reason_required: True; blind_review_attested: False; raw_content_excluded_attested: True; reviewer_reason_required_attested: True; ready_for_human_evidence: False; raw_content_allowed: False; raw_content_path_count: 0; accepted: False
    提交态
    status: missing; path: evidence/world_class/submissions/human-adjudication.json; attested_real_evidence: False; privacy_contract_satisfied: False

    执行步骤

    • python3 scripts/yao.py output-review-kit --write-template
    • Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.
    • python3 scripts/adjudicate_output_review.py --write-template
    • Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.
    • python3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • python3 scripts/yao.py output-review
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    完成定义

    • reports/output_review_adjudication.json summary.pending_count == 0
    • reports/output_review_adjudication.json summary.judgment_count == summary.pair_count
    • reports/output_review_adjudication.json summary.invalid_decision_count == 0
    • reports/output_review_adjudication.json summary.reviewer_metadata_present is true
    • reports/output_review_adjudication.json summary.blind_review_attested is true
    • reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json
    • reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text
    • reports/output_review_adjudication.json summary.ready_for_human_evidence is true
    • reports/skill_os2_audit.json item human-adjudication status becomes pass

    证据来源

    • reports/output_blind_review_pack.md
    • reports/output_review_kit.md
    • reports/output_review_decisions.json
    • reports/output_review_adjudication.json
    • reports/output_review_adjudication.md
    • scripts/import_output_review_decisions.py
    • evidence/world_class/intake.schema.json
    • evidence/world_class/templates/human-adjudication.intake.json
    • reports/world_class_evidence_intake.json
    • reports/world_class_evidence_intake.md

    隐私约束

    • Reviewer decisions should not include raw user data or private customer detail.
    • Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.
    • The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.
    • The adjudication evidence stores prompt_sha256 instead of raw prompt text.
    • The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.
    • Keep the answer key separate until after decisions are recorded.

    源证据检查

    • Review pairs existpair_count: 5 / >0Generate the blind A/B review pack.
    • No pending decisionspending_count: 5 / ==0Record a reviewer choice and reason for every pair.
    • Judgments completejudgment_count: 0 / ==pair_countEvery pair needs one valid human judgment.
    • No invalid decisionsinvalid_decision_count: 0 / ==0Fix malformed winner/confidence entries.
    • Reviewer metadatareviewer_metadata_present: False / trueRecord reviewer and reviewed_at before adjudication can count.
    • Reason requiredreason_required: True / trueKeep reason mandatory for every imported or direct reviewer decision.
    • Blind review attestedblind_review_attested: False / trueSet reviewer_attestation only after choices are completed before opening the answer key.
    • Raw content attestedraw_content_excluded_attested: True / trueAttest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content.
    • Raw content blockedraw_content_allowed: False / falseAdjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages.
    • Human evidence readyready_for_human_evidence: False / trueComplete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.
    待补证 · external

    Native Permission Enforcement

    Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.

    负责人
    target client or installer integrator
    当前状态
    native-enforced targets 0; installer-enforced targets 4
    下一步
    Integrate a real target-client or external installer runtime guard before claiming native permission enforcement.
    观测值
    native_enforcement_count: 0; metadata_fallback_count: 4; installer_enforcement_pass_count: 4; installer_permission_failure_count: 0; installer_enforcement_ready: True; residual_risk_count: 4; failure_count: 0; accepted: False
    提交态
    status: missing; path: evidence/world_class/submissions/native-permission-enforcement.json; attested_real_evidence: False; privacy_contract_satisfied: False

    执行步骤

    • Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.
    • Update the generated target adapter only when the guard is actually enforced by that target.
    • python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip
    • python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation
    • python3 scripts/yao.py runtime-permissions . --package-dir dist
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    完成定义

    • reports/runtime_permission_probes.json summary.native_enforcement_count > 0
    • reports/runtime_permission_probes.json summary.failure_count == 0
    • reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence
    • reports/skill_os2_audit.json item native-permission-enforcement status becomes pass

    证据来源

    • dist/targets/*/adapter.json
    • reports/runtime_permission_probes.json
    • reports/runtime_permission_probes.md
    • reports/install_simulation.json
    • reports/install_simulation.md
    • security/permission_policy.json
    • evidence/world_class/intake.schema.json
    • evidence/world_class/templates/native-permission-enforcement.intake.json
    • reports/world_class_evidence_intake.json
    • reports/world_class_evidence_intake.md

    隐私约束

    • Do not mark native_enforcement true for metadata-only fallbacks.
    • Keep residual risks visible for targets that still rely on operator enforcement.

    源证据检查

    • Native enforcementnative_enforcement_count: 0 / >0Collect real target-client or external runtime guard proof.
    • Probe failuresfailure_count: 0 / ==0Runtime permission probes must stay clean.
    • Installer supportinstaller_enforcement_ready: True / trueInstaller enforcement is supporting evidence, not native proof.
    待补证 · external

    Native Client Telemetry

    Import production metadata-only events from a real external client into the local drift loop.

    负责人
    Browser/Chrome/IDE/provider client integrator
    当前状态
    external source events 0; adoption samples 1
    下一步
    Install a real client against the native host and import production metadata-only events.
    观测值
    external_source_events: 0; adoption_sample_count: 1; raw_content_allowed: False; risk_band: low; accepted: False
    提交态
    status: missing; path: evidence/world_class/submissions/native-client-telemetry.json; attested_real_evidence: False; privacy_contract_satisfied: False

    执行步骤

    • python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension://<extension-id>/
    • Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.
    • python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl
    • python3 scripts/yao.py skill-atlas --workspace-root .
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    完成定义

    • reports/adoption_drift_report.json summary.source_types.external > 0
    • reports/adoption_drift_report.json summary.adoption_sample_count > 0
    • reports/skill_os2_audit.json item native-client-telemetry status becomes pass

    证据来源

    • reports/adoption_drift_report.json
    • reports/adoption_drift_report.md
    • reports/telemetry_hook_recipes.json
    • scripts/telemetry_native_host.py
    • evidence/world_class/intake.schema.json
    • evidence/world_class/templates/native-client-telemetry.intake.json
    • reports/world_class_evidence_intake.json
    • reports/world_class_evidence_intake.md

    隐私约束

    • Telemetry must remain metadata-only and local-first.
    • Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field.

    源证据检查

    • External eventsexternal_source_events: 0 / >0Import at least one metadata-only event from a real client.
    • Adoption sampleadoption_sample_count: 1 / >0Telemetry must include adoption outcome evidence.
    • Raw content blockedraw_content_allowed: False / falseTelemetry must stay metadata-only.
    +
    + +
    +

    提交清单

    +

    每张卡片给出模板、提交文件、准备命令、校验命令、收集要求、通过条件和隐私边界;只有真实 provider、真人、原生权限或真实客户端证据通过 intake 后,才进入 ledger review。

    +
    awaiting-submission · external

    Provider Holdout

    No real evidence submission has been provided yet.

    负责人
    operator with provider credentials
    模板
    evidence/world_class/templates/provider-holdout.intake.json
    提交
    evidence/world_class/submissions/provider-holdout.json
    下一步
    Run provider-backed holdout cases with real credentials and commit only aggregate evidence.

    操作命令

    • prepare_submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions
    • validate_intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • submission_reviewpython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh_ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard_claimpython3 scripts/yao.py world-class-claim-guard .

    收集要求

    • provider-backed model run
    • observed timing
    • observed token metadata

    执行步骤

    • Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    • For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60
    • For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    通过条件

    • reports/output_execution_runs.json summary.model_executed_count > 0
    • reports/output_execution_runs.json summary.timing_observed_count > 0
    • reports/output_execution_runs.json summary.token_observed_count > 0
    • reports/skill_os2_audit.json item provider-holdout status becomes pass

    隐私边界

    • Do not commit provider credentials or environment dumps.
    • The output execution report records output hashes and aggregate run metadata, not raw provider prompts.
    awaiting-submission · human

    Human Adjudication

    No real evidence submission has been provided yet.

    负责人
    human reviewer
    模板
    evidence/world_class/templates/human-adjudication.intake.json
    提交
    evidence/world_class/submissions/human-adjudication.json
    下一步
    Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication.

    操作命令

    • prepare_submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions
    • validate_intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • submission_reviewpython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh_ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard_claimpython3 scripts/yao.py world-class-claim-guard .

    收集要求

    • real reviewer identity
    • blind A/B decisions
    • answer key unopened until decisions exist

    执行步骤

    • python3 scripts/yao.py output-review-kit --write-template
    • Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.
    • python3 scripts/adjudicate_output_review.py --write-template
    • Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.
    • python3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • python3 scripts/yao.py output-review
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    通过条件

    • reports/output_review_adjudication.json summary.pending_count == 0
    • reports/output_review_adjudication.json summary.judgment_count == summary.pair_count
    • reports/output_review_adjudication.json summary.invalid_decision_count == 0
    • reports/output_review_adjudication.json summary.reviewer_metadata_present is true
    • reports/output_review_adjudication.json summary.blind_review_attested is true
    • reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json
    • reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text
    • reports/output_review_adjudication.json summary.ready_for_human_evidence is true
    • reports/skill_os2_audit.json item human-adjudication status becomes pass

    隐私边界

    • Reviewer decisions should not include raw user data or private customer detail.
    • Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.
    • The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.
    • The adjudication evidence stores prompt_sha256 instead of raw prompt text.
    • The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.
    • Keep the answer key separate until after decisions are recorded.
    awaiting-submission · external

    Native Permission Enforcement

    No real evidence submission has been provided yet.

    负责人
    target client or installer integrator
    模板
    evidence/world_class/templates/native-permission-enforcement.intake.json
    提交
    evidence/world_class/submissions/native-permission-enforcement.json
    下一步
    Integrate a real target-client or external installer runtime guard before claiming native permission enforcement.

    操作命令

    • prepare_submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions
    • validate_intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • submission_reviewpython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh_ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard_claimpython3 scripts/yao.py world-class-claim-guard .

    收集要求

    • real target client or external installer runtime guard
    • native enforcement flag or externally accepted guard proof
    • residual risk retained for fallback targets

    执行步骤

    • Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.
    • Update the generated target adapter only when the guard is actually enforced by that target.
    • python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip
    • python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation
    • python3 scripts/yao.py runtime-permissions . --package-dir dist
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    通过条件

    • reports/runtime_permission_probes.json summary.native_enforcement_count > 0
    • reports/runtime_permission_probes.json summary.failure_count == 0
    • reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence
    • reports/skill_os2_audit.json item native-permission-enforcement status becomes pass

    隐私边界

    • Do not mark native_enforcement true for metadata-only fallbacks.
    • Keep residual risks visible for targets that still rely on operator enforcement.
    awaiting-submission · external

    Native Client Telemetry

    No real evidence submission has been provided yet.

    负责人
    Browser/Chrome/IDE/provider client integrator
    模板
    evidence/world_class/templates/native-client-telemetry.intake.json
    提交
    evidence/world_class/submissions/native-client-telemetry.json
    下一步
    Install a real client against the native host and import production metadata-only events.

    操作命令

    • prepare_submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions
    • validate_intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • submission_reviewpython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh_ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard_claimpython3 scripts/yao.py world-class-claim-guard .

    收集要求

    • real external client source
    • metadata-only event
    • local-first import path

    执行步骤

    • python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension://<extension-id>/
    • Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.
    • python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl
    • python3 scripts/yao.py skill-atlas --workspace-root .
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions

    通过条件

    • reports/adoption_drift_report.json summary.source_types.external > 0
    • reports/adoption_drift_report.json summary.adoption_sample_count > 0
    • reports/skill_os2_audit.json item native-client-telemetry status becomes pass

    隐私边界

    • Telemetry must remain metadata-only and local-first.
    • Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field.
    +
    + +
    +

    蓝图覆盖

    项目数
    21
    模块数
    8
    建议 PR
    13
    通过数
    21
    Warn Count
    0
    缺失数
    0
    Extension Track Count
    4
    Extension Covered Count
    4
    Extension Partial Count
    0
    Extension Planned Count
    0
    Adaptive Extension Ready
    本地蓝图
    世界级
    待补证据
    4
    +

    覆盖边界

    蓝图覆盖只证明 2.0 模块、建议 PR、脚本、报告和测试在本地闭环;public world-class 仍以 world-class evidence ledger 的真人和外部证据为准。

    +
    + +
    +

    公开声明

    本地复现
    发布锁
    可公开声明
    声明阻断
    4
    Provider 证据
    人审完成
    世界级就绪
    +

    声明阻断

    • 阻断release lock is not clean or commit is unavailable
    • 阻断human blind-review adjudication is incomplete
    • 阻断world-class evidence is not accepted yet (3 open gaps, 4 ledger pending)
    • 阻断world-class source checks are not all accepted (12/19 pass, 7 blocked)
    +
    + +
    +

    世界证据

    4 pending world-class evidence entries; 1 human pending; 3 external pending; source checks 12/19 pass; 7 blocked; overclaim guard true

    +

    证据台账

    Ledger Entry Count
    4
    Accepted Count
    0
    待审
    4
    Human Pending Count
    1
    External Pending Count
    3
    Overclaim Guard Active
    Ready To Claim World Class
    +
    + +
    +

    证据入口

    Schema
    模板数
    4
    模板通过
    4
    提交数
    0
    有效提交
    0
    无效提交
    0
    可收证据
    可审台账
    Ready To Claim World Class
    +

    入口边界

    intake 只校验证据包格式、来源、隐私和反过度声明;只有 ledger 看到真实 provider、真人、原生权限或真实客户端结果后,world-class 才能进入完成审计。

    +
    + +
    +

    声明守卫

    台账可声明
    台账待补
    4
    声明面
    201
    违规数
    0
    Overclaim Guard Active
    +

    声明边界

    claim guard 扫描 README、docs 和 reports 中的完成态表述;ledger 未 ready 时,任何英文完成断言、true 状态声明或中文完成态都会阻断发布审查。

    +
    + +
    +

    注册审计

    yao-meta-skill 1.1.0; 6/6 compatibility entries pass; install pass with 4 adapters; installer permissions 12 enforced / 0 failures

    +

    包体元数据

    名称
    yao-meta-skill
    版本
    1.1.0
    Maturity
    governed
    Owner
    Yao Team
    License
    MIT
    信任级别
    local
    目标平台
    openai, claude, generic, agent-skills-compatible, vscode
    兼容通过
    6/6
    归档哈希
    c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7
    +
    + +
    +

    发布路线

    0 promote; 3 keep current; 0 blocked; upgrade minor declared / minor recommended

    +

    包体验证

    目标数
    4
    Adapter
    4
    归档存在
    Zip 条目
    580
    失败数
    0
    警告数
    0
    归档哈希
    c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7
    +
    + +
    +

    安装模拟

    Archive Extracted
    Entrypoint Loaded
    Manifest Loaded
    Interface Loaded
    Adapter
    4
    安装权限
    12
    安装权限失败
    0
    权限目标
    4
    权限能力
    3
    失败数
    0
    警告数
    0
    +

    权限覆盖

    安装模拟会读取解压后的 target adapter,并按 declared_capabilities 核对 security/permission_policy.json 中的有效批准、证据、过期时间和目标端 enforcement 说明。

    +
    +
    + + diff --git a/reports/review-studio.json b/reports/review-studio.json new file mode 100644 index 0000000..2db5773 --- /dev/null +++ b/reports/review-studio.json @@ -0,0 +1,28783 @@ +{ + "schema_version": "2.0", + "ok": true, + "skill_dir": ".", + "summary": { + "decision": "review", + "world_class_score": 91, + "gate_count": 16, + "gate_contract_ok": true, + "blocker_count": 0, + "warning_count": 3, + "action_count": 3, + "annotation_count": 0, + "open_annotation_count": 0, + "open_annotation_blocker_count": 0, + "open_annotation_warning_count": 0 + }, + "gates": [ + { + "key": "intent-canvas", + "label": "意图画布", + "status": "pass", + "detail": "intent confidence 100/100; Intent is clear enough to package the first routeable version.", + "evidence": "reports/intent-confidence.json", + "link": "intent-confidence.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "trigger-lab", + "label": "触发实验", + "status": "pass", + "detail": "13 trigger cases; 0 misroutes; 0 ambiguous", + "evidence": "reports/route_scorecard.json", + "link": "route_scorecard.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "output-lab", + "label": "输出实验", + "status": "warn", + "detail": "5/5 cases; with-skill 100.0; baseline 0.0; file-backed 1; near-neighbor 1; blind A/B 5; exec 10; command 10; model 10; recorded 0; reviewed 0/5; review pending 5", + "evidence": "reports/output_quality_scorecard.json", + "link": "output_quality_scorecard.md", + "review_action_id": "review-action-output-lab", + "review_action_status": "warn", + "review_action_title": "输出实验", + "review_action_next_step": "补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。同步补足盲审声明。", + "review_action_reason": "没有输出质量和人工盲评证据时,Skill 只能证明会触发,不能证明输出真的更好且经得起审查。", + "review_action_source_ref_count": 6, + "review_action_verification_command": "python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review" + }, + { + "key": "context-budget", + "label": "上下文", + "status": "pass", + "detail": "initial load 987/1000; deferred 531767/120000; top deferred scripts 469568; resource governance governed; quality density 141.8", + "evidence": "reports/context_budget.json", + "link": "context_budget.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "runtime-matrix", + "label": "运行矩阵", + "status": "pass", + "detail": "5 / 5 targets pass", + "evidence": "reports/conformance_matrix.json", + "link": "conformance_matrix.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "trust-report", + "label": "信任报告", + "status": "pass", + "detail": "0 secrets; 155 scripts; 3 network-capable scripts; 0 help smoke failures", + "evidence": "reports/security_trust_report.json", + "link": "security_trust_report.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "python-compat", + "label": "Python 兼容", + "status": "pass", + "detail": "Python 3.11; 235 files; 0 compatibility issues; 0 syntax; 0 f-string 3.11 hazards", + "evidence": "reports/python_compatibility.json", + "link": "python_compatibility.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "architecture-maintainability", + "label": "架构维护", + "status": "pass", + "detail": "232 Python files; 0 hotspots; 0 watchlist files; 6 early watch files; 0 blockers; largest 719 lines; 71 CLI handlers; 18 in entrypoint", + "evidence": "reports/architecture_maintainability.json", + "link": "architecture_maintainability.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "permission-gates", + "label": "权限批准", + "status": "pass", + "detail": "3/3 permissions approved; gaps 0; required file_write, network, subprocess", + "evidence": "reports/security_trust_report.json + security/permission_policy.json", + "link": "../security/permission_policy.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "permission-runtime", + "label": "权限探针", + "status": "pass", + "detail": "4/4 targets probed; native 0; metadata fallback 4; installer 4; residual risks 4", + "evidence": "reports/runtime_permission_probes.json", + "link": "runtime_permission_probes.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "skill-atlas", + "label": "组合治理", + "status": "pass", + "detail": "11 skills, 1 actionable; 0 actionable route collisions; 0 actionable owner gaps; 0 actionable stale; 0 actionable drift; 22 scoped non-actionable issues", + "evidence": "reports/skill_atlas.json", + "link": "skill_atlas.html", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "operations-loop", + "label": "运营回路", + "status": "pass", + "detail": "1 metadata events; adoption 100.0; missed 0; bad-output 0; risk low; daily proposals 5; daily decision proposal-review; daily release lock false; weekly queue 5 unique; weekly ready 1; weekly top 88; weekly release lock false", + "evidence": "reports/adoption_drift_report.json + reports/skillops/daily + reports/skillops/weekly", + "link": "adoption_drift_report.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "review-waivers", + "label": "人工批准", + "status": "warn", + "detail": "0 active waivers; 1 warning gates still need reviewer decision", + "evidence": "reports/review_waivers.json", + "link": "review_waivers.md", + "review_action_id": "review-action-review-waivers", + "review_action_status": "warn", + "review_action_title": "人工批准", + "review_action_next_step": "对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。", + "review_action_reason": "warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。", + "review_action_source_ref_count": 2, + "review_action_verification_command": "python3 scripts/render_review_waivers.py ." + }, + { + "key": "world-class-evidence", + "label": "世界证据", + "status": "warn", + "detail": "4 pending world-class evidence entries; 1 human pending; 3 external pending; source checks 12/19 pass; 7 blocked; overclaim guard true", + "evidence": "reports/world_class_evidence_ledger.json", + "link": "world_class_evidence_ledger.md", + "review_action_id": "review-action-world-class-evidence", + "review_action_status": "warn", + "review_action_title": "世界证据", + "review_action_next_step": "补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据,或明确本次发布不声明 world-class 完成。", + "review_action_reason": "世界级结论必须来自已接受的外部/人工证据;计划、metadata fallback、待评审和本地命令都不能替代完成证据。", + "review_action_source_ref_count": 16, + "review_action_verification_command": "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 ." + }, + { + "key": "registry-audit", + "label": "注册审计", + "status": "pass", + "detail": "yao-meta-skill 1.1.0; 6/6 compatibility entries pass; install pass with 4 adapters; installer permissions 12 enforced / 0 failures", + "evidence": "reports/registry_audit.json + reports/install_simulation.json", + "link": "registry_audit.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + }, + { + "key": "release-notes", + "label": "发布路线", + "status": "pass", + "detail": "0 promote; 3 keep current; 0 blocked; upgrade minor declared / minor recommended", + "evidence": "reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md", + "link": "promotion_decisions.md", + "review_action_id": "", + "review_action_status": "", + "review_action_title": "", + "review_action_next_step": "", + "review_action_reason": "", + "review_action_source_ref_count": 0, + "review_action_verification_command": "" + } + ], + "gate_contract": { + "ok": true, + "expected_gate_count": 16, + "actual_gate_count": 16, + "expected_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "review-waivers", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "world-class-evidence" + ], + "rendered_gate_keys": [ + "intent-canvas", + "trigger-lab", + "output-lab", + "context-budget", + "runtime-matrix", + "trust-report", + "python-compat", + "architecture-maintainability", + "permission-gates", + "permission-runtime", + "skill-atlas", + "operations-loop", + "review-waivers", + "world-class-evidence", + "registry-audit", + "release-notes" + ], + "missing_gate_keys": [], + "unknown_gate_keys": [], + "duplicate_gate_keys": [], + "unweighted_gate_keys": [] + }, + "blockers": [], + "warnings": [ + { + "key": "output-lab", + "label": "输出实验", + "status": "warn", + "detail": "5/5 cases; with-skill 100.0; baseline 0.0; file-backed 1; near-neighbor 1; blind A/B 5; exec 10; command 10; model 10; recorded 0; reviewed 0/5; review pending 5", + "evidence": "reports/output_quality_scorecard.json", + "link": "output_quality_scorecard.md", + "review_action_id": "review-action-output-lab", + "review_action_status": "warn", + "review_action_title": "输出实验", + "review_action_next_step": "补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。同步补足盲审声明。", + "review_action_reason": "没有输出质量和人工盲评证据时,Skill 只能证明会触发,不能证明输出真的更好且经得起审查。", + "review_action_source_ref_count": 6, + "review_action_verification_command": "python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review" + }, + { + "key": "review-waivers", + "label": "人工批准", + "status": "warn", + "detail": "0 active waivers; 1 warning gates still need reviewer decision", + "evidence": "reports/review_waivers.json", + "link": "review_waivers.md", + "review_action_id": "review-action-review-waivers", + "review_action_status": "warn", + "review_action_title": "人工批准", + "review_action_next_step": "对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。", + "review_action_reason": "warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。", + "review_action_source_ref_count": 2, + "review_action_verification_command": "python3 scripts/render_review_waivers.py ." + }, + { + "key": "world-class-evidence", + "label": "世界证据", + "status": "warn", + "detail": "4 pending world-class evidence entries; 1 human pending; 3 external pending; source checks 12/19 pass; 7 blocked; overclaim guard true", + "evidence": "reports/world_class_evidence_ledger.json", + "link": "world_class_evidence_ledger.md", + "review_action_id": "review-action-world-class-evidence", + "review_action_status": "warn", + "review_action_title": "世界证据", + "review_action_next_step": "补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据,或明确本次发布不声明 world-class 完成。", + "review_action_reason": "世界级结论必须来自已接受的外部/人工证据;计划、metadata fallback、待评审和本地命令都不能替代完成证据。", + "review_action_source_ref_count": 16, + "review_action_verification_command": "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 ." + } + ], + "review_actions": [ + { + "action_id": "review-action-output-lab", + "gate_key": "output-lab", + "title": "输出实验", + "label": "输出实验", + "status": "warn", + "priority": "warning", + "next_step": "补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。同步补足盲审声明。", + "summary": "补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。同步补足盲审声明。", + "reason": "没有输出质量和人工盲评证据时,Skill 只能证明会触发,不能证明输出真的更好且经得起审查。", + "why": "没有输出质量和人工盲评证据时,Skill 只能证明会触发,不能证明输出真的更好且经得起审查。", + "source_fix": "evals/output/cases.jsonl + reports/output_quality_scorecard.md + reports/output_review_kit.html + reports/output_review_adjudication.md + reports/output_review_decisions.json", + "source_refs": [ + { + "path": "evals/output/cases.jsonl", + "label": "output eval cases", + "kind": "eval", + "line": 1, + "matched_pattern": "\"id\"", + "excerpt": "{\"id\":\"skill-package-contract\",\"prompt\":\"Turn this repeated workflow into a reusable team skill package.\",\"baseline_output\":\"I can write a prompt for that workflow and include a s…", + "exists": true, + "link": "../evals/output/cases.jsonl" + }, + { + "path": "reports/output_quality_scorecard.md", + "label": "output scorecard", + "kind": "report", + "line": 1, + "matched_pattern": "# Output", + "excerpt": "# Output Quality Scorecard", + "exists": true, + "link": "output_quality_scorecard.md" + }, + { + "path": "reports/output_execution_runs.md", + "label": "output execution runs", + "kind": "report", + "line": 1, + "matched_pattern": "# Output Execution", + "excerpt": "# Output Execution Runs", + "exists": true, + "link": "output_execution_runs.md" + }, + { + "path": "reports/output_blind_review_pack.md", + "label": "blind A/B review pack", + "kind": "report", + "line": 1, + "matched_pattern": "# Output Blind", + "excerpt": "# Output Blind A/B Review Pack", + "exists": true, + "link": "output_blind_review_pack.md" + }, + { + "path": "reports/output_review_kit.html", + "label": "reviewer cockpit", + "kind": "report", + "line": 6, + "matched_pattern": "Output Review Kit", + "excerpt": "Output Review Kit", + "exists": true, + "link": "output_review_kit.html" + }, + { + "path": "reports/output_review_adjudication.md", + "label": "review adjudication", + "kind": "report", + "line": 1, + "matched_pattern": "# Output Review", + "excerpt": "# Output Review Adjudication", + "exists": true, + "link": "output_review_adjudication.md" + } + ], + "evidence": "reports/output_quality_scorecard.json", + "evidence_link": "output_quality_scorecard.md", + "verification_command": "python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review", + "evidence_steps": [] + }, + { + "action_id": "review-action-review-waivers", + "gate_key": "review-waivers", + "title": "人工批准", + "label": "人工批准", + "status": "warn", + "priority": "warning", + "next_step": "对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。", + "summary": "对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。", + "reason": "warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。", + "why": "warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。", + "source_fix": "reports/review_waivers.md", + "source_refs": [ + { + "path": "reports/review_waivers.md", + "label": "waiver ledger", + "kind": "report", + "line": 1, + "matched_pattern": "# Review", + "excerpt": "# Review Waivers", + "exists": true, + "link": "review_waivers.md" + }, + { + "path": "references/review-waiver-method.md", + "label": "waiver method", + "kind": "method", + "line": 1, + "matched_pattern": "# Review", + "excerpt": "# Review Waiver Method", + "exists": true, + "link": "../references/review-waiver-method.md" + } + ], + "evidence": "reports/review_waivers.json", + "evidence_link": "review_waivers.md", + "verification_command": "python3 scripts/render_review_waivers.py .", + "evidence_steps": [] + }, + { + "action_id": "review-action-world-class-evidence", + "gate_key": "world-class-evidence", + "title": "世界证据", + "label": "世界证据", + "status": "warn", + "priority": "warning", + "next_step": "补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据,或明确本次发布不声明 world-class 完成。", + "summary": "补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据,或明确本次发布不声明 world-class 完成。", + "reason": "世界级结论必须来自已接受的外部/人工证据;计划、metadata fallback、待评审和本地命令都不能替代完成证据。", + "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_refs": [ + { + "path": "reports/world_class_evidence_ledger.md", + "label": "world-class evidence ledger", + "kind": "report", + "line": 1, + "matched_pattern": "# World-Class Evidence Ledger", + "excerpt": "# World-Class Evidence Ledger", + "exists": true, + "link": "world_class_evidence_ledger.md" + }, + { + "path": "reports/world_class_evidence_plan.md", + "label": "world-class evidence plan", + "kind": "report", + "line": 1, + "matched_pattern": "# World-Class Evidence Plan", + "excerpt": "# World-Class Evidence Plan", + "exists": true, + "link": "world_class_evidence_plan.md" + }, + { + "path": "reports/world_class_evidence_intake.md", + "label": "world-class evidence intake", + "kind": "report", + "line": 1, + "matched_pattern": "# World-Class Evidence Intake", + "excerpt": "# World-Class Evidence Intake", + "exists": true, + "link": "world_class_evidence_intake.md" + }, + { + "path": "reports/world_class_evidence_preflight.md", + "label": "world-class evidence preflight", + "kind": "report", + "line": 1, + "matched_pattern": "# World-Class Evidence Preflight", + "excerpt": "# World-Class Evidence Preflight", + "exists": true, + "link": "world_class_evidence_preflight.md" + }, + { + "path": "reports/world_class_evidence_preflight.html", + "label": "world-class preflight HTML", + "kind": "report", + "line": 6, + "matched_pattern": "World-Class Evidence Preflight", + "excerpt": "World-Class Evidence Preflight", + "exists": true, + "link": "world_class_evidence_preflight.html" + }, + { + "path": "reports/world_class_submission_review.md", + "label": "world-class submission review", + "kind": "report", + "line": 1, + "matched_pattern": "# World-Class Submission Review", + "excerpt": "# World-Class Submission Review", + "exists": true, + "link": "world_class_submission_review.md" + }, + { + "path": "reports/world_class_claim_guard.md", + "label": "world-class claim guard", + "kind": "report", + "line": 1, + "matched_pattern": "# World-Class Claim Guard", + "excerpt": "# World-Class Claim Guard", + "exists": true, + "link": "world_class_claim_guard.md" + }, + { + "path": "evidence/world_class/intake.schema.json", + "label": "evidence intake schema", + "kind": "schema", + "line": 4, + "matched_pattern": "Yao World-Class Evidence Intake", + "excerpt": "\"title\": \"Yao World-Class Evidence Intake\",", + "exists": true, + "link": "../evidence/world_class/intake.schema.json" + }, + { + "path": "evidence/world_class/templates/provider-holdout.intake.json", + "label": "provider intake template", + "kind": "template", + "line": 3, + "matched_pattern": "provider-holdout", + "excerpt": "\"evidence_key\": \"provider-holdout\",", + "exists": true, + "link": "../evidence/world_class/templates/provider-holdout.intake.json" + }, + { + "path": "evidence/world_class/templates/human-adjudication.intake.json", + "label": "human intake template", + "kind": "template", + "line": 3, + "matched_pattern": "human-adjudication", + "excerpt": "\"evidence_key\": \"human-adjudication\",", + "exists": true, + "link": "../evidence/world_class/templates/human-adjudication.intake.json" + }, + { + "path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "label": "permission intake template", + "kind": "template", + "line": 3, + "matched_pattern": "native-permission-enforcement", + "excerpt": "\"evidence_key\": \"native-permission-enforcement\",", + "exists": true, + "link": "../evidence/world_class/templates/native-permission-enforcement.intake.json" + }, + { + "path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "label": "telemetry intake template", + "kind": "template", + "line": 3, + "matched_pattern": "native-client-telemetry", + "excerpt": "\"evidence_key\": \"native-client-telemetry\",", + "exists": true, + "link": "../evidence/world_class/templates/native-client-telemetry.intake.json" + }, + { + "path": "reports/skill_os2_audit.md", + "label": "Skill OS 2.0 audit", + "kind": "report", + "line": 1, + "matched_pattern": "# Skill OS", + "excerpt": "# Skill OS 2.0 Audit", + "exists": true, + "link": "skill_os2_audit.md" + }, + { + "path": "reports/output_review_decisions.json", + "label": "human review decisions", + "kind": "report", + "line": 30, + "matched_pattern": "winner_variant", + "excerpt": "\"winner_variant\": \"Use A or B after reading the blind review pack. Leave blank when pending.\",", + "exists": true, + "link": "output_review_decisions.json" + }, + { + "path": "reports/runtime_permission_probes.md", + "label": "runtime permission probes", + "kind": "report", + "line": 1, + "matched_pattern": "# Runtime", + "excerpt": "# Runtime Permission Probes", + "exists": true, + "link": "runtime_permission_probes.md" + }, + { + "path": "reports/adoption_drift_report.md", + "label": "adoption drift", + "kind": "report", + "line": 1, + "matched_pattern": "# Adoption", + "excerpt": "# Adoption And Drift Report", + "exists": true, + "link": "adoption_drift_report.md" + } + ], + "evidence": "reports/world_class_evidence_ledger.json", + "evidence_link": "world_class_evidence_ledger.md", + "verification_command": "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 .", + "evidence_steps": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "status": "pending", + "readiness": "awaiting-submission", + "current": "model-executed 10; token-observed 10", + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence.", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "source_pass_count": 3, + "source_blocked_count": 0, + "blocked_checks": [], + "repair_rows": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "repair_blocked_count": 1, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "phase_queue_blocked_count": 1, + "phase_queue_counts_as_completion": false, + "commands": [ + { + "key": "prepare_submission", + "label": "准备提交", + "command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions" + }, + { + "key": "validate_intake", + "label": "校验入口", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "submission_review", + "label": "审查提交", + "command": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "refresh_ledger", + "label": "刷新台账", + "command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "guard_claim", + "label": "声明守卫", + "command": "python3 scripts/yao.py world-class-claim-guard ." + } + ], + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120" + ], + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json" + ], + "artifact_role_contract": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_ready_count": 1, + "submission_ref_total_count": 1, + "supporting_evidence_ready_count": 6, + "supporting_evidence_total_count": 6, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "ready_count": 1, + "total_count": 1, + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "ready_count": 6, + "total_count": 6, + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ] + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "status": "pending", + "readiness": "awaiting-submission", + "current": "0/5 decisions; pending 5", + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication.", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "source_pass_count": 5, + "source_blocked_count": 5, + "blocked_checks": [ + { + "label": "No pending decisions", + "field": "pending_count", + "actual": 5, + "expected": "==0", + "status": "blocked", + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "label": "Judgments complete", + "field": "judgment_count", + "actual": 0, + "expected": "==pair_count", + "status": "blocked", + "next_action": "Every pair needs one valid human judgment." + }, + { + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "actual": false, + "expected": "true", + "status": "blocked", + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "label": "Blind review attested", + "field": "blind_review_attested", + "actual": false, + "expected": "true", + "status": "blocked", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "actual": false, + "expected": "true", + "status": "blocked", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "repair_rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "repair_blocked_count": 6, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "commands": [ + { + "key": "prepare_submission", + "label": "准备提交", + "command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions" + }, + { + "key": "validate_intake", + "label": "校验入口", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "submission_review", + "label": "审查提交", + "command": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "refresh_ledger", + "label": "刷新台账", + "command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "guard_claim", + "label": "声明守卫", + "command": "python3 scripts/yao.py world-class-claim-guard ." + } + ], + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template" + ], + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md" + ], + "artifact_role_contract": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_ready_count": 2, + "submission_ref_total_count": 2, + "supporting_evidence_ready_count": 8, + "supporting_evidence_total_count": 8, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "ready_count": 2, + "total_count": 2, + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "ready_count": 8, + "total_count": 8, + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text." + ] + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "status": "pending", + "readiness": "awaiting-submission", + "current": "native-enforced targets 0; installer-enforced targets 4", + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement.", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "source_pass_count": 2, + "source_blocked_count": 1, + "blocked_checks": [ + { + "label": "Native enforcement", + "field": "native_enforcement_count", + "actual": 0, + "expected": ">0", + "status": "blocked", + "next_action": "Collect real target-client or external runtime guard proof." + } + ], + "repair_rows": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "repair_blocked_count": 2, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "commands": [ + { + "key": "prepare_submission", + "label": "准备提交", + "command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions" + }, + { + "key": "validate_intake", + "label": "校验入口", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "submission_review", + "label": "审查提交", + "command": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "refresh_ledger", + "label": "刷新台账", + "command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "guard_claim", + "label": "声明守卫", + "command": "python3 scripts/yao.py world-class-claim-guard ." + } + ], + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip" + ], + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md" + ], + "artifact_role_contract": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_ready_count": 2, + "submission_ref_total_count": 2, + "supporting_evidence_ready_count": 11, + "supporting_evidence_total_count": 11, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "ready_count": 2, + "total_count": 2, + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "ready_count": 11, + "total_count": 11, + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ] + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "status": "pending", + "readiness": "awaiting-submission", + "current": "external source events 0; adoption samples 1", + "next_action": "Install a real client against the native host and import production metadata-only events.", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "source_pass_count": 2, + "source_blocked_count": 1, + "blocked_checks": [ + { + "label": "External events", + "field": "external_source_events", + "actual": 0, + "expected": ">0", + "status": "blocked", + "next_action": "Import at least one metadata-only event from a real client." + } + ], + "repair_rows": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "repair_blocked_count": 2, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "commands": [ + { + "key": "prepare_submission", + "label": "准备提交", + "command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions" + }, + { + "key": "validate_intake", + "label": "校验入口", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "submission_review", + "label": "审查提交", + "command": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "refresh_ledger", + "label": "刷新台账", + "command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions" + }, + { + "key": "guard_claim", + "label": "声明守卫", + "command": "python3 scripts/yao.py world-class-claim-guard ." + } + ], + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl" + ], + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json" + ], + "artifact_role_contract": { + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_ready_count": 2, + "submission_ref_total_count": 2, + "supporting_evidence_ready_count": 6, + "supporting_evidence_total_count": 6, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "ready_count": 2, + "total_count": 2, + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "ready_count": 6, + "total_count": 6, + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ] + } + ] + } + ], + "evidence_paths": { + "skill_overview": "reports/skill-overview.html", + "review_viewer": "reports/review-viewer.html", + "output_eval": "reports/output_quality_scorecard.md", + "output_execution": "reports/output_execution_runs.md", + "output_blind_review": "reports/output_blind_review_pack.md", + "output_review_kit": "reports/output_review_kit.md", + "output_review_kit_html": "reports/output_review_kit.html", + "output_review_decisions": "reports/output_review_decisions.json", + "output_review_adjudication": "reports/output_review_adjudication.md", + "benchmark_reproducibility": "reports/benchmark_reproducibility.md", + "skill_os2_coverage": "reports/skill_os2_coverage.md", + "runtime_conformance": "reports/conformance_matrix.md", + "trust_report": "reports/security_trust_report.md", + "python_compatibility": "reports/python_compatibility.md", + "architecture_maintainability": "reports/architecture_maintainability.md", + "permission_policy": "security/permission_policy.md", + "runtime_permissions": "reports/runtime_permission_probes.md", + "skill_atlas": "reports/skill_atlas.html", + "compiled_targets": "reports/compiled_targets.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "adaptation_proposals": "reports/adaptation_proposals.md", + "adaptation_approval_ledger": "reports/adaptation_approval_ledger.json", + "adaptation_regression": "reports/adaptation_regression_report.md", + "world_class_evidence_plan": "reports/world_class_evidence_plan.md", + "world_class_evidence_ledger": "reports/world_class_evidence_ledger.md", + "world_class_evidence_intake": "reports/world_class_evidence_intake.md", + "world_class_evidence_preflight": "reports/world_class_evidence_preflight.md", + "world_class_evidence_preflight_html": "reports/world_class_evidence_preflight.html", + "world_class_submission_review": "reports/world_class_submission_review.md", + "world_class_operator_runbook": "reports/world_class_operator_runbook.md", + "world_class_operator_runbook_html": "reports/world_class_operator_runbook.html", + "world_class_claim_guard": "reports/world_class_claim_guard.md", + "registry_audit": "reports/registry_audit.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "upgrade_check": "reports/upgrade_check.md", + "migration": "docs/migration-v2.md", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "daily_skillops": "reports/skillops/daily/2026-07-02.md", + "weekly_curator": "reports/skillops/weekly/2026-W27.md" + }, + "data": { + "overview": { + "skill_summary": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "maturity": "governed", + "updated_at": "2026-03-31", + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": [ + "SKILL.md", + "agents/interface.yaml", + "skill-ir/examples/yao-meta-skill.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_adjudication.md", + "reports/benchmark_reproducibility.md", + "reports/output_blind_answer_key.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/world_class_evidence_plan.md", + "reports/world_class_evidence_ledger.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-interpretation.html", + "reports/skill-overview.html" + ], + "flow": [ + "输入材料", + "Skill 包体", + "可复用能力" + ] + }, + "scorecard": { + "completeness_score": { + "label": "完整度", + "score": 100, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 100, + "reasons": [ + "已生成 20 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 100, + "reasons": [ + "SKILL.md 约 314 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 已承载确定性逻辑。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 42, + "reasons": [ + "入口约 314 个词/字,references 约 16934 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本偏高,建议压缩入口或拆分 references。" + ] + } + }, + "capability_profile": { + "archetype": "governed", + "task_family": "Execution operation", + "maturity": "governed", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "principle_model": { + "nodes": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "contract_boundary": { + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "quality_review": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "gaps": [ + "上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + } + }, + "risk_governance": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 1, + "signal": "SKILL.md 约 314 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "world_class_readiness": { + "ready": false, + "decision": "evidence-pending", + "entry_count": 4, + "pending_count": 4, + "accepted_count": 0, + "external_pending_count": 3, + "human_pending_count": 1, + "source_check_count": 19, + "source_pass_count": 12, + "conclusion_zh": "世界级证据尚未完成:4 项待补,0 项已接受。", + "conclusion_en": "World-class evidence is not complete: 4 pending, 0 accepted.", + "entries": [ + { + "key": "provider-holdout", + "label_zh": "提供商留出", + "label_en": "Provider Holdout", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "缺少真实 provider 模型运行和 token metadata。", + "summary_en": "Missing a real provider model run and token metadata.", + "blocked_checks": [] + }, + { + "key": "human-adjudication", + "label_zh": "人工盲评", + "label_en": "Human Adjudication", + "category": "human", + "category_zh": "人工证据", + "category_en": "Human evidence", + "status": "pending", + "summary_zh": "盲评 pair 仍待真实 reviewer 决策。", + "summary_en": "Blind-review pairs still need real reviewer decisions.", + "blocked_checks": [ + "No pending decisions", + "Judgments complete", + "Reviewer metadata" + ] + }, + { + "key": "native-permission-enforcement", + "label_zh": "原生权限", + "label_en": "Native Permission", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "原生 runtime enforcement 仍待目标客户端或外部安装器证明。", + "summary_en": "Native runtime enforcement still needs target-client or external-installer proof.", + "blocked_checks": [ + "Native enforcement" + ] + }, + { + "key": "native-client-telemetry", + "label_zh": "原生遥测", + "label_en": "Native Telemetry", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "真实外部客户端 metadata-only 事件仍未导入。", + "summary_en": "Real external-client metadata-only events have not been imported yet.", + "blocked_checks": [ + "External events" + ] + } + ] + }, + "package_assets": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "file_count": 393, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 35 + }, + { + "label": "scripts", + "value": 155 + }, + { + "label": "evals", + "value": 29 + }, + { + "label": "reports", + "value": 170 + } + ] + }, + "iteration_roadmap": { + "items": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ] + }, + "report_contract": { + "schema_version": "2.0", + "html_report": "reports/skill-overview.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "layout": "kami-white-audit-v2", + "nav_labels": [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线" + ], + "nav_labels_en": [ + "Overview", + "Metrics", + "Profile", + "Principle", + "Contract", + "Quality", + "Risk", + "Assets", + "Roadmap" + ] + }, + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "logic_steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "usage_steps": [ + "Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.", + "Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + ], + "package_map": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "scenarios": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals." + ], + "trigger_contract": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "io_contract": { + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ] + }, + "principles": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "roadmap": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ], + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。" + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": "2.0.0", + "source_path": "skill-ir/examples/yao-meta-skill.json", + "target_count": 5, + "trigger_samples": 8, + "output_eval_cases": 3 + }, + "compiled_targets": { + "ok": true, + "schema_version": "1.0", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter_mode": "metadata-adapter", + "degradation_strategy": "metadata-adapter", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "permission_enforcement": "metadata-only", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "adapter_mode": "neutral-source-plus-adapter", + "degradation_strategy": "neutral-source-plus-adapter", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "permission_enforcement": "metadata-fallback", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [] + }, + { + "target": "generic", + "status": "pass", + "adapter_mode": "agent-skills-compatible", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills compatible neutral package", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "targets/generic/adapter.json" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "agent-skills-compatible", + "status": "pass", + "adapter_mode": "neutral-agent-skills-source", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills standard source tree", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "vscode", + "status": "pass", + "adapter_mode": "vscode-agent-skills-adapter", + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "permission_enforcement": "client-or-workspace-trust", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [] + } + ], + "failures": [], + "warnings": [] + }, + "output_quality": { + "case_count": 5, + "file_backed_case_count": 1, + "near_neighbor_case_count": 1, + "boundary_case_count": 1, + "baseline_pass_rate": 0.0, + "with_skill_pass_rate": 100.0, + "delta": 100.0, + "regression_count": 0, + "gate_pass": true, + "failure_taxonomy": [], + "blind_pair_count": 5 + }, + "output_execution": { + "ok": true, + "summary": { + "case_count": 5, + "variant_run_count": 10, + "command_executed_count": 10, + "model_executed_count": 10, + "recorded_fixture_count": 0, + "timing_observed_count": 10, + "token_observed_count": 10, + "token_estimated_count": 0, + "baseline_pass_rate": 23.67, + "with_skill_pass_rate": 43.67, + "delta": 20.0, + "regression_count": 0, + "failure_count": 0, + "gate_pass": true + }, + "runner": { + "mode": "command", + "command": [ + "python3", + "scripts/provider_output_eval_runner.py", + "--provider", + "deepseek", + "--model", + "deepseek-v4-flash", + "--api-format", + "chat-completions", + "--thinking", + "disabled", + "--api-key-env", + "DEEPSEEK_API_KEY" + ], + "timeout_seconds": 180.0 + }, + "failures": [] + }, + "output_blind_review": { + "summary": { + "pair_count": 5, + "answer_key_separate": true, + "with_skill_hidden_count": 5 + }, + "seed": "yao-output-eval-blind-v1", + "pair_count": 5, + "answer_key_separate": true + }, + "output_review_kit": { + "ok": true, + "summary": { + "case_count": 5, + "ready_for_adjudication_count": 0, + "pending_decision_count": 5, + "invalid_decision_count": 0, + "reviewer_metadata_present": false, + "answer_key_hidden": true, + "answer_key_path_exposed": false, + "ready_to_run_adjudication": false, + "failure_count": 0 + }, + "artifacts": { + "reviewer_kit_json": "reports/output_review_kit.json", + "reviewer_kit_markdown": "reports/output_review_kit.md", + "reviewer_kit_html": "reports/output_review_kit.html", + "blind_pack_json": "reports/output_blind_review_pack.json", + "blind_pack_markdown": "reports/output_blind_review_pack.md", + "decisions": "reports/output_review_decisions.json", + "adjudication_json": "reports/output_review_adjudication.json", + "adjudication_markdown": "reports/output_review_adjudication.md" + }, + "failures": [] + }, + "output_review_adjudication": { + "ok": true, + "summary": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "agreement_count": 0, + "disagreement_count": 0, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "pending_answer_hidden_count": 5, + "agreement_rate": null, + "needs_review": true, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "failure_count": 0, + "reviewer_checklist_count": 5, + "reviewer_checklist_pending_count": 5, + "reviewer_checklist_invalid_count": 0, + "reviewer_checklist_ready_count": 0 + }, + "reviewer": "", + "reviewed_at": "", + "failures": [] + }, + "benchmark_reproducibility": { + "ok": true, + "summary": { + "reproducibility_ready": true, + "release_lock_ready": false, + "methodology_complete": true, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "evidence_bundle_sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158", + "source_contract_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "output_case_count": 5, + "failure_disclosure_count": 3, + "command_count": 23, + "command_executed_count": 10, + "timing_observed_count": 10, + "model_executed_count": 10, + "token_observed_count": 10, + "human_review_complete": false, + "provider_evidence_complete": true, + "world_class_ready": false, + "world_class_open_gap_count": 3, + "world_class_task_count": 4, + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4, + "working_tree_dirty": true, + "changed_file_count": 37, + "source_tree_dirty": true, + "source_changed_file_count": 1, + "generated_tree_dirty": true, + "generated_changed_file_count": 36 + }, + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "missing_artifacts": [], + "limitations": [ + "The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately.", + "Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet.", + "Pending blind-review decisions are visible but do not count as human adjudication.", + "World-class readiness remains false until external and human evidence gaps close.", + "Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims." + ] + }, + "runtime_conformance": { + "target_count": 5, + "pass_count": 5, + "fail_count": 0 + }, + "runtime_permissions": { + "ok": true, + "summary": { + "target_count": 4, + "pass_count": 4, + "fail_count": 0, + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "residual_risk_count": 4, + "required_capability_count": 3, + "failure_count": 0, + "installer_enforcement_source_status": "present", + "installer_enforcement_target_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "installer_permission_capability_count": 3, + "world_class_native_evidence_ready": false, + "installer_enforcement_ready": true + }, + "expected_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "targets": [ + { + "target": "openai", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "claude", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "generic", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "vscode", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + } + ], + "failures": [] + }, + "trust_security": { + "scanned_files": 247, + "script_count": 155, + "internal_module_count": 69, + "secret_findings": 0, + "dependency_files": [ + "requirements-ci.txt" + ], + "network_script_count": 3, + "network_policy_covered_count": 3, + "network_policy_missing_count": 0, + "file_write_script_count": 74, + "permission_required_count": 3, + "permission_approved_count": 3, + "permission_missing_count": 0, + "permission_invalid_count": 0, + "permission_expired_count": 0, + "help_smoke_checked_count": 86, + "help_smoke_failed_count": 0, + "interactive_script_count": 0, + "package_hash_scope": "source-contract-without-generated-reports", + "package_hash_file_count": 247, + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + }, + "skill_atlas": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + }, + "registry_distribution": { + "ok": true, + "package": { + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "agent-skills": "pass", + "vscode": "pass", + "generic": "pass", + "agent-skills-compatible": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "package_verification": "reports/package_verification.json", + "install_simulated": true, + "install_simulation": "reports/install_simulation.json" + }, + "generated_at": "2026-06-13" + }, + "failures": [], + "warnings": [] + }, + "package_verification": { + "ok": true, + "summary": { + "target_count": 4, + "adapter_count": 4, + "archive_present": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "archive_entry_count": 580, + "nested_skill_entry_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "install_simulation": { + "ok": true, + "summary": { + "archive_present": true, + "archive_entry_count": 598, + "nested_skill_entry_count": 0, + "archive_extracted": true, + "entrypoint_loaded": true, + "manifest_loaded": true, + "interface_loaded": true, + "adapter_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "install_root_is_temp": false, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "upgrade_check": { + "ok": true, + "summary": { + "declared_bump": "minor", + "recommended_bump": "minor", + "breaking_change_count": 0, + "added_target_count": 2, + "metadata_change_count": 1, + "checksum_change_count": 2, + "failure_count": 0, + "warning_count": 0 + }, + "upgrade_diff": { + "name_changed": false, + "added_targets": [ + "agent-skills-compatible", + "vscode" + ], + "removed_targets": [], + "compatibility_changes": [ + { + "target": "agent-skills", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "agent-skills-compatible", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "vscode", + "from": "missing", + "to": "pass", + "direction": "improved" + } + ], + "metadata_changes": [ + { + "field": "description", + "from": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes.", + "to": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + } + ], + "checksum_changes": [ + { + "field": "archive_sha256", + "from": "", + "to": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + { + "field": "package_sha256", + "from": "0000000000000000000000000000000000000000000000000000000000000000", + "to": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "release_notes": [ + "Recommended version bump: minor.", + "Added targets: agent-skills-compatible, vscode.", + "Package or archive checksum changed; reviewers should verify package artifacts before release.", + "Package verification evidence: reports/package_verification.md." + ], + "failures": [], + "warnings": [] + }, + "adoption_drift": { + "ok": true, + "summary": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 100.0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "failure_types": {}, + "source_types": { + "manual": 1 + }, + "command_counts": {} + }, + "next_iteration_candidates": [], + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "failures": [] + }, + "review_waivers": { + "ok": true, + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 2, + "waiverable_open_count": 1, + "non_waivable_count": 1 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "review_studio_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "review-waivers", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "world-class-evidence" + ], + "known_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "waiverable_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "non_waivable_gate_keys": [ + "review-waivers", + "world-class-evidence" + ] + }, + "failures": [], + "warnings": [] + }, + "review_annotations": { + "ok": true, + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [] + }, + "world_class_evidence_plan": { + "ok": true, + "summary": { + "audit_decision": "continue-iteration", + "world_class_ready": false, + "audit_world_class_ready": false, + "ready_to_claim_world_class": false, + "ledger_completion_required": true, + "evidence_requirement_count": 4, + "task_count": 4, + "human_task_count": 1, + "external_task_count": 3, + "review_task_count": 0, + "decision": "collect-external-evidence" + }, + "tasks": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "status": "pass", + "category": "external", + "owner": "operator with provider credentials", + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "audit_next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "category": "human", + "owner": "human reviewer", + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "audit_next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "category": "external", + "owner": "target client or installer integrator", + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "audit_next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "audit_next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_audit": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "open_gap_count": 3 + } + }, + "world_class_evidence_ledger": { + "ok": true, + "summary": { + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "entries": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "pending", + "source_status": "pass", + "source_accepted": true, + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "pending", + "source_status": "human_required", + "source_accepted": false, + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_plan": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "task_count": 4, + "evidence_requirement_count": 4 + } + }, + "synthesis_highlights": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + }, + "metadata": { + "canonical_format": "agent-skills", + "targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "maturity_tier": "governed", + "skill_archetype": "governed", + "updated_at": "2026-03-31" + } + }, + "intent_confidence": { + "score": 100, + "band": "high", + "gate_passed": true, + "strengths": [ + "The recurring job is concrete enough to anchor the package.", + "Real input shape is explicit.", + "The hand-back output is concrete.", + "Boundary exclusions are already explicit.", + "Operational constraints are visible." + ], + "gaps": [], + "follow_up_questions": [], + "anchor_sentence": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse. Primary output: A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.. Exclusions: one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill, general code review or debugging unless the user is packaging that workflow as a skill, raw private material that was not intentionally supplied as skill evidence, platform-specific plugin creation when the request is not about an agent skill package.", + "recommended_action": "Intent is clear enough to package the first routeable version.", + "context": { + "job": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "real_inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "primary_output": "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "exclusions": [ + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill", + "raw private material that was not intentionally supplied as skill evidence", + "platform-specific plugin creation when the request is not about an agent skill package" + ], + "constraints": [ + "keep SKILL.md lean and route primarily through frontmatter description", + "put durable guidance in references, executable logic in scripts, and evidence in reports", + "default to the lightest reliable mode before adding governance weight", + "preserve portability across OpenAI, Claude, generic, and Agent Skills compatible targets", + "avoid raw prompt, output, transcript, or private content in telemetry" + ], + "standards": [ + "trigger boundaries must be tested with should-trigger and should-not-trigger cases", + "production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence", + "governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation", + "generated reports should be bilingual or reviewer-friendly when they are user-facing", + "each new asset must earn its place by reducing ambiguity, risk, or repeated work" + ], + "correction": "The 2.0 direction reframes this package from a Meta Skill factory into a Skill OS with Skill IR, compiler contracts, output eval, runtime conformance, trust, registry, Review Studio, Skill Atlas, and telemetry loops.", + "user_references": [ + "Yao Meta Skill 2.0 upgrade plan", + "Kami-style white report layout requirements for generated skill reports", + "local Waza engineering habits for review, design, diagnosis, and implementation discipline" + ] + } + }, + "intent_dialogue": { + "skill_name": "yao-meta-skill", + "title": "Yao Meta Skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "focus": "portability-and-contract", + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", + "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster." + ], + "opening_styles": [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。" + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。" + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。" + } + ], + "optional_scaffold": [ + "The repeated job it should reliably handle", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse" + ], + "questions": [ + { + "question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?", + "why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape." + }, + { + "question": "When someone reaches for this skill in the real world, what materials will they actually hand to it?", + "why": "Input shape decides whether references, scripts, or templates are needed." + }, + { + "question": "What finished output should it hand back so the user can immediately keep moving?", + "why": "Outputs should drive the package structure before extra guidance is added." + }, + { + "question": "Which nearby requests should this skill politely refuse so the boundary stays clean?", + "why": "The exclusion list is the fastest route to better trigger quality." + }, + { + "question": "What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?", + "why": "Constraints decide how much structure, packaging, and review the skill actually needs." + }, + { + "question": "Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example?", + "why": "A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material." + }, + { + "question": "Which environments or clients must be able to consume this skill?", + "why": "This sets the minimum metadata and degradation strategy." + } + ], + "output": { + "capability_sentence": "Yao Meta Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", + "required_capture": [ + "recurring job", + "real inputs", + "required outputs", + "exclusions", + "constraints", + "reference preferences", + "first evaluation target" + ], + "recommended_first_gate": "portability and contract" + } + }, + "route_scorecard": { + "summary": { + "total_cases": 13, + "correct_cases": 13, + "accuracy": 1.0, + "ambiguous_case_count": 0, + "margin_warning_threshold": 0.08, + "misroute_count": 0, + "no_route_accuracy": 1.0 + }, + "route_stats": { + "yao-meta-skill": { + "expected_count": 3, + "predicted_count": 3, + "correct_count": 3, + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.644 + }, + "team-frontend-review": { + "expected_count": 3, + "predicted_count": 3, + "correct_count": 3, + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.805 + }, + "governed-incident-command": { + "expected_count": 3, + "predicted_count": 3, + "correct_count": 3, + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.709 + }, + "no_route": { + "expected_count": 4, + "predicted_count": 4, + "correct_count": 4, + "precision": 1.0, + "recall": 1.0, + "average_margin": null + } + }, + "confusion_matrix": { + "yao-meta-skill": { + "yao-meta-skill": 3, + "team-frontend-review": 0, + "governed-incident-command": 0, + "no_route": 0 + }, + "team-frontend-review": { + "yao-meta-skill": 0, + "team-frontend-review": 3, + "governed-incident-command": 0, + "no_route": 0 + }, + "governed-incident-command": { + "yao-meta-skill": 0, + "team-frontend-review": 0, + "governed-incident-command": 3, + "no_route": 0 + }, + "no_route": { + "yao-meta-skill": 0, + "team-frontend-review": 0, + "governed-incident-command": 0, + "no_route": 4 + } + }, + "ambiguous_cases": [], + "misroutes": [], + "cases": [ + { + "family": "meta_create", + "prompt": "Create a reusable skill from this workflow and add evals before sharing it with the team.", + "expected_route": "yao-meta-skill", + "predicted_route": "yao-meta-skill", + "correct": true, + "margin": 0.681, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.681, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "meta_iterate", + "prompt": "Refactor this existing skill description, tighten the trigger boundary, and package it for reuse.", + "expected_route": "yao-meta-skill", + "predicted_route": "yao-meta-skill", + "correct": true, + "margin": 0.51, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.51, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.011, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "meta_operationalize", + "prompt": "Turn these prompts, notes, and transcripts into a maintained skill package with references and scripts.", + "expected_route": "yao-meta-skill", + "predicted_route": "yao-meta-skill", + "correct": true, + "margin": 0.74, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.74, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "build_skill", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.181, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [ + "governed_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.011, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "frontend_premerge", + "prompt": "Run a pre-merge frontend review for keyboard navigation issues and unsafe rendering.", + "expected_route": "team-frontend-review", + "predicted_route": "team-frontend-review", + "correct": true, + "margin": 0.893, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.893, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "ui_security", + "ux_regression" + ], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.012, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.006, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "frontend_states", + "prompt": "Run a pre-merge frontend review on this React diff for missing empty states, form handling, and UX regressions.", + "expected_route": "team-frontend-review", + "predicted_route": "team-frontend-review", + "correct": true, + "margin": 0.704, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.704, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend", + "ui_security", + "ux_regression" + ], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.008, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.004, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "frontend_a11y", + "prompt": "Review React components for a11y regressions, unsafe rendering, and UI behavior before merge.", + "expected_route": "team-frontend-review", + "predicted_route": "team-frontend-review", + "correct": true, + "margin": 0.819, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.819, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "ui_security" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.006, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.006, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "incident_packet", + "prompt": "Build a governed incident command packet from alerts, responder notes, incident communication needs, and severity findings.", + "expected_route": "governed-incident-command", + "predicted_route": "governed-incident-command", + "correct": true, + "margin": 1.0, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 1.0, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.011, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.005, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "incident_severity", + "prompt": "Run severity assessment and package incident communication from this outage review.", + "expected_route": "governed-incident-command", + "predicted_route": "governed-incident-command", + "correct": true, + "margin": 0.505, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 0.505, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "comms_and_owners", + "severity_assessment" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.179, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [ + "reuse_package" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.022, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "incident_governed_workflow", + "prompt": "Standardize this incident review into a reusable governed workflow with owner actions.", + "expected_route": "governed-incident-command", + "predicted_route": "governed-incident-command", + "correct": true, + "margin": 0.621, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 1.0, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.379, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.013, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "no_route_explain", + "prompt": "Explain keyboard accessibility basics in plain English.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.195, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [ + "accessibility" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ] + }, + { + "name": "governed-incident-command", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "no_route_build", + "prompt": "Build a React form page with loading and empty states.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 0.008, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [ + "frontend_scope" + ], + "matched_negative_concepts": [ + "build_ui" + ] + } + ] + }, + { + "family": "no_route_single_update", + "prompt": "Write one short outage update for customers from these notes.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.016, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.008, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "no_route_document_only", + "prompt": "Summarize this workflow and keep it as documentation only.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.009, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [ + "transform_workflow" + ], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ] + } + ] + } + ] + }, + "output_quality": { + "ok": true, + "cases": "evals/output/cases.jsonl", + "summary": { + "case_count": 5, + "file_backed_case_count": 1, + "near_neighbor_case_count": 1, + "boundary_case_count": 1, + "baseline_pass_rate": 0.0, + "with_skill_pass_rate": 100.0, + "delta": 100.0, + "regression_count": 0, + "gate_pass": true, + "failure_taxonomy": [], + "blind_pair_count": 5 + }, + "results": [ + { + "id": "skill-package-contract", + "prompt": "Turn this repeated workflow into a reusable team skill package.", + "input_files": [], + "metadata": {}, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "checks": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0, + "failure_type": "missing_entrypoint", + "passed": false, + "missing": [ + "SKILL.md" + ], + "present_forbidden": [] + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0, + "failure_type": "missing_interface", + "passed": false, + "missing": [ + "agents/interface.yaml" + ], + "present_forbidden": [] + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0, + "failure_type": "missing_review_artifact", + "passed": false, + "missing": [ + "reports/skill-overview.html" + ], + "present_forbidden": [] + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0, + "failure_type": "resource_boundary", + "passed": false, + "missing": [ + "references/", + "scripts/", + "reports/" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0, + "failure_type": "missing_entrypoint", + "passed": false, + "missing": [ + "SKILL.md" + ], + "present_forbidden": [] + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0, + "failure_type": "missing_interface", + "passed": false, + "missing": [ + "agents/interface.yaml" + ], + "present_forbidden": [] + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0, + "failure_type": "missing_review_artifact", + "passed": false, + "missing": [ + "reports/skill-overview.html" + ], + "present_forbidden": [] + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0, + "failure_type": "resource_boundary", + "passed": false, + "missing": [ + "references/", + "scripts/", + "reports/" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 4, + "failed_count": 0, + "checks": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0, + "failure_type": "missing_entrypoint", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0, + "failure_type": "missing_interface", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0, + "failure_type": "missing_review_artifact", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0, + "failure_type": "resource_boundary", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "output-eval-expectation", + "prompt": "Upgrade this production skill so we know whether the generated output is better than baseline.", + "input_files": [], + "metadata": {}, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "checks": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0, + "failure_type": "missing_baseline_delta", + "passed": false, + "missing": [ + "with-skill", + "baseline" + ], + "present_forbidden": [] + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0, + "failure_type": "missing_assertions", + "passed": false, + "missing": [ + "assertion grading" + ], + "present_forbidden": [] + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0, + "failure_type": "missing_scorecard", + "passed": false, + "missing": [ + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0, + "failure_type": "missing_failure_taxonomy", + "passed": false, + "missing": [ + "failure taxonomy" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0, + "failure_type": "missing_baseline_delta", + "passed": false, + "missing": [ + "with-skill", + "baseline" + ], + "present_forbidden": [] + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0, + "failure_type": "missing_assertions", + "passed": false, + "missing": [ + "assertion grading" + ], + "present_forbidden": [] + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0, + "failure_type": "missing_scorecard", + "passed": false, + "missing": [ + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0, + "failure_type": "missing_failure_taxonomy", + "passed": false, + "missing": [ + "failure taxonomy" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 4, + "failed_count": 0, + "checks": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0, + "failure_type": "missing_baseline_delta", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0, + "failure_type": "missing_assertions", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0, + "failure_type": "missing_scorecard", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0, + "failure_type": "missing_failure_taxonomy", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "ir-before-packaging", + "prompt": "Package this skill for OpenAI, Claude, Agent Skills, and generic targets.", + "input_files": [], + "metadata": {}, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "checks": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0, + "failure_type": "missing_ir", + "passed": false, + "missing": [ + "Skill IR first" + ], + "present_forbidden": [] + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0, + "failure_type": "incomplete_ir_contract", + "passed": false, + "missing": [ + "job_to_be_done", + "trigger_surface", + "eval_plan", + "governance" + ], + "present_forbidden": [] + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0, + "failure_type": "missing_targets", + "passed": false, + "missing": [ + "OpenAI", + "Claude", + "Agent Skills", + "generic" + ], + "present_forbidden": [] + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0, + "failure_type": "semantic_drift", + "passed": false, + "missing": [ + "preserve the same capability contract" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0, + "failure_type": "missing_ir", + "passed": false, + "missing": [ + "Skill IR first" + ], + "present_forbidden": [] + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0, + "failure_type": "incomplete_ir_contract", + "passed": false, + "missing": [ + "job_to_be_done", + "trigger_surface", + "eval_plan", + "governance" + ], + "present_forbidden": [] + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0, + "failure_type": "missing_targets", + "passed": false, + "missing": [ + "OpenAI", + "Claude", + "Agent Skills", + "generic" + ], + "present_forbidden": [] + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0, + "failure_type": "semantic_drift", + "passed": false, + "missing": [ + "preserve the same capability contract" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 4, + "failed_count": 0, + "checks": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0, + "failure_type": "missing_ir", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0, + "failure_type": "incomplete_ir_contract", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0, + "failure_type": "missing_targets", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0, + "failure_type": "semantic_drift", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "near-neighbor-boundary", + "prompt": "I only need a one-off summary of these notes, not a reusable process.", + "input_files": [], + "metadata": { + "case_type": "near_neighbor", + "tier": "production" + }, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 3, + "checks": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0, + "failure_type": "over_trigger", + "passed": false, + "missing": [ + "Do not create a skill" + ], + "present_forbidden": [] + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0, + "failure_type": "missing_boundary_language", + "passed": false, + "missing": [ + "near-neighbor" + ], + "present_forbidden": [] + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0, + "failure_type": "missing_reuse_gate", + "passed": false, + "missing": [ + "repeated use", + "reusable output contract" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0, + "failure_type": "over_trigger", + "passed": false, + "missing": [ + "Do not create a skill" + ], + "present_forbidden": [] + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0, + "failure_type": "missing_boundary_language", + "passed": false, + "missing": [ + "near-neighbor" + ], + "present_forbidden": [] + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0, + "failure_type": "missing_reuse_gate", + "passed": false, + "missing": [ + "repeated use", + "reusable output contract" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 3, + "failed_count": 0, + "checks": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0, + "failure_type": "over_trigger", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0, + "failure_type": "missing_boundary_language", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0, + "failure_type": "missing_reuse_gate", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + }, + { + "id": "file-backed-governed-package", + "prompt": "Turn the attached release brief source into a governed skill package.", + "input_files": [ + "fixtures/release-brief-source.md" + ], + "metadata": { + "case_type": "boundary", + "tier": "governed" + }, + "baseline": { + "score": 0.0, + "passed_count": 0, + "failed_count": 5, + "checks": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0, + "failure_type": "missing_file_fixture", + "passed": false, + "missing": [ + "file-backed fixture", + "input_files" + ], + "present_forbidden": [] + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0, + "failure_type": "missing_governance", + "passed": false, + "missing": [ + "review cadence" + ], + "present_forbidden": [] + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0, + "failure_type": "missing_contract_boundary", + "passed": false, + "missing": [ + "output contract", + "rollback boundary" + ], + "present_forbidden": [] + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0, + "failure_type": "missing_release_evidence", + "passed": false, + "missing": [ + "trust report", + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0, + "failure_type": "invented_evidence", + "passed": false, + "missing": [ + "missing evidence" + ], + "present_forbidden": [] + } + ], + "failed": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0, + "failure_type": "missing_file_fixture", + "passed": false, + "missing": [ + "file-backed fixture", + "input_files" + ], + "present_forbidden": [] + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0, + "failure_type": "missing_governance", + "passed": false, + "missing": [ + "review cadence" + ], + "present_forbidden": [] + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0, + "failure_type": "missing_contract_boundary", + "passed": false, + "missing": [ + "output contract", + "rollback boundary" + ], + "present_forbidden": [] + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0, + "failure_type": "missing_release_evidence", + "passed": false, + "missing": [ + "trust report", + "reports/output_quality_scorecard.md" + ], + "present_forbidden": [] + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0, + "failure_type": "invented_evidence", + "passed": false, + "missing": [ + "missing evidence" + ], + "present_forbidden": [] + } + ] + }, + "with_skill": { + "score": 100.0, + "passed_count": 5, + "failed_count": 0, + "checks": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0, + "failure_type": "missing_file_fixture", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0, + "failure_type": "missing_governance", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0, + "failure_type": "missing_contract_boundary", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0, + "failure_type": "missing_release_evidence", + "passed": true, + "missing": [], + "present_forbidden": [] + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0, + "failure_type": "invented_evidence", + "passed": true, + "missing": [], + "present_forbidden": [] + } + ], + "failed": [] + }, + "delta": 100.0, + "winner": "with_skill", + "failure_taxonomy": [] + } + ], + "failures": [], + "blind_review": { + "pack": "reports/output_blind_review_pack.json", + "answer_key": "reports/output_blind_answer_key.json", + "pair_count": 5 + }, + "artifacts": { + "json": "reports/output_quality_scorecard.json", + "markdown": "reports/output_quality_scorecard.md", + "blind_review_pack_json": "reports/output_blind_review_pack.json", + "blind_review_pack_md": "reports/output_blind_review_pack.md", + "blind_answer_key_json": "reports/output_blind_answer_key.json" + } + }, + "output_execution": { + "schema_version": "1.0", + "ok": true, + "cases": "evals/output/cases.jsonl", + "runner": { + "mode": "command", + "command": [ + "python3", + "scripts/provider_output_eval_runner.py", + "--provider", + "deepseek", + "--model", + "deepseek-v4-flash", + "--api-format", + "chat-completions", + "--thinking", + "disabled", + "--api-key-env", + "DEEPSEEK_API_KEY" + ], + "timeout_seconds": 180.0 + }, + "summary": { + "case_count": 5, + "variant_run_count": 10, + "command_executed_count": 10, + "model_executed_count": 10, + "recorded_fixture_count": 0, + "timing_observed_count": 10, + "token_observed_count": 10, + "token_estimated_count": 0, + "baseline_pass_rate": 23.67, + "with_skill_pass_rate": 43.67, + "delta": 20.0, + "regression_count": 0, + "failure_count": 0, + "gate_pass": true + }, + "runs": [ + { + "case_id": "skill-package-contract", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 5980.01, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 100, + "output_tokens": 384, + "total_tokens": 484, + "estimated": false + }, + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "failed_assertions": [ + "has-entrypoint", + "has-interface", + "has-report", + "has-resource-boundary" + ], + "output_sha256": "1aa12d65d55863581eef67aaa173b3a9962524a91533693044cb340a342b760b", + "failure": "" + }, + { + "case_id": "skill-package-contract", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 11217.19, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 850, + "output_tokens": 998, + "total_tokens": 1848, + "estimated": false + }, + "score": 75.0, + "passed_count": 3, + "failed_count": 1, + "failed_assertions": [ + "has-report" + ], + "output_sha256": "ac2cb822520b3099150ea1df07de59b9c6df382b7251792e0662631a60989d89", + "failure": "" + }, + { + "case_id": "output-eval-expectation", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 3178.95, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 107, + "output_tokens": 129, + "total_tokens": 236, + "estimated": false + }, + "score": 0.0, + "passed_count": 0, + "failed_count": 4, + "failed_assertions": [ + "has-baseline-comparison", + "has-assertion-grading", + "has-scorecard", + "has-failure-taxonomy" + ], + "output_sha256": "a128ff4dbd7870e287ac651b6cab6b7887e347d6477e5bd8c6f40cd9479539f0", + "failure": "" + }, + { + "case_id": "output-eval-expectation", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 4611.78, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 857, + "output_tokens": 264, + "total_tokens": 1121, + "estimated": false + }, + "score": 25.0, + "passed_count": 1, + "failed_count": 3, + "failed_assertions": [ + "has-baseline-comparison", + "has-assertion-grading", + "has-failure-taxonomy" + ], + "output_sha256": "4df98d20e2199e56dae2618b6a64e59d5c96484a4f71ae0ebc674b755cf533c3", + "failure": "" + }, + { + "case_id": "ir-before-packaging", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 6772.59, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 104, + "output_tokens": 661, + "total_tokens": 765, + "estimated": false + }, + "score": 25.0, + "passed_count": 1, + "failed_count": 3, + "failed_assertions": [ + "has-ir-first", + "has-core-ir-fields", + "has-semantic-preservation" + ], + "output_sha256": "1696075b4f884ff8b478786b5565aff73ad73235936eb39c2ed615c0746861d2", + "failure": "" + }, + { + "case_id": "ir-before-packaging", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 17471.19, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 854, + "output_tokens": 2132, + "total_tokens": 2986, + "estimated": false + }, + "score": 25.0, + "passed_count": 1, + "failed_count": 3, + "failed_assertions": [ + "has-ir-first", + "has-core-ir-fields", + "has-semantic-preservation" + ], + "output_sha256": "4f87633e53f360948d6403126d49ad3dabcb1b47aff596918650719b7840abcf", + "failure": "" + }, + { + "case_id": "near-neighbor-boundary", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 3091.33, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 105, + "output_tokens": 93, + "total_tokens": 198, + "estimated": false + }, + "score": 33.33, + "passed_count": 1, + "failed_count": 2, + "failed_assertions": [ + "declines-skill", + "requires-reuse-signal" + ], + "output_sha256": "56c8f48f3b2b9963adad3afdb1b6a2bed6b162067506edee1e0f1acd32668a91", + "failure": "" + }, + { + "case_id": "near-neighbor-boundary", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 4152.85, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 855, + "output_tokens": 160, + "total_tokens": 1015, + "estimated": false + }, + "score": 33.33, + "passed_count": 1, + "failed_count": 2, + "failed_assertions": [ + "declines-skill", + "requires-reuse-signal" + ], + "output_sha256": "d2d85a2aae5db5c4c7e6e0db5bdd865157a90018afe302b0a703940cc84ba68c", + "failure": "" + }, + { + "case_id": "file-backed-governed-package", + "variant": "baseline", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 4750.01, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 200, + "output_tokens": 302, + "total_tokens": 502, + "estimated": false + }, + "score": 60.0, + "passed_count": 3, + "failed_count": 2, + "failed_assertions": [ + "uses-file-backed-evidence", + "has-trust-and-scorecard" + ], + "output_sha256": "d41880b0c2f7e60ee6acb2cb1f4acb9377fd63de72b8e026e6259a962b89af0b", + "failure": "" + }, + { + "case_id": "file-backed-governed-package", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": true, + "command_executed": true, + "duration_ms": 9406.25, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 950, + "output_tokens": 660, + "total_tokens": 1610, + "estimated": false + }, + "score": 60.0, + "passed_count": 3, + "failed_count": 2, + "failed_assertions": [ + "has-governance", + "has-output-contract" + ], + "output_sha256": "c670ae2ef81a819a9a0f1b0c046e82ba4c649f56dcbe96a45a6938c7f214fba4", + "failure": "" + } + ], + "failures": [], + "artifacts": { + "json": "reports/output_execution_runs.json", + "markdown": "reports/output_execution_runs.md" + } + }, + "output_blind_review": { + "schema_version": "1.0", + "seed": "yao-output-eval-blind-v1", + "summary": { + "pair_count": 5, + "answer_key_separate": true, + "with_skill_hidden_count": 5 + }, + "pairs": [ + { + "case_id": "skill-package-contract", + "prompt": "Turn this repeated workflow into a reusable team skill package.", + "input_files": [], + "metadata": {}, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0 + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0 + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0 + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "skill-package-contract:A", + "output": "Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/." + }, + "variant_b": { + "blind_id": "skill-package-contract:B", + "output": "I can write a prompt for that workflow and include a short checklist." + } + }, + { + "case_id": "output-eval-expectation", + "prompt": "Upgrade this production skill so we know whether the generated output is better than baseline.", + "input_files": [], + "metadata": {}, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0 + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0 + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0 + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "output-eval-expectation:A", + "output": "Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md." + }, + "variant_b": { + "blind_id": "output-eval-expectation:B", + "output": "Add more examples and run the trigger eval again." + } + }, + { + "case_id": "ir-before-packaging", + "prompt": "Package this skill for OpenAI, Claude, Agent Skills, and generic targets.", + "input_files": [], + "metadata": {}, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0 + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0 + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0 + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "ir-before-packaging:A", + "output": "Export folders for each platform and adjust files as needed." + }, + "variant_b": { + "blind_id": "ir-before-packaging:B", + "output": "Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract." + } + }, + { + "case_id": "near-neighbor-boundary", + "prompt": "I only need a one-off summary of these notes, not a reusable process.", + "input_files": [], + "metadata": { + "case_type": "near_neighbor", + "tier": "production" + }, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0 + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0 + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "near-neighbor-boundary:A", + "output": "Create a SKILL.md and a reusable workflow anyway so future notes can use it." + }, + "variant_b": { + "blind_id": "near-neighbor-boundary:B", + "output": "Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract." + } + }, + { + "case_id": "file-backed-governed-package", + "prompt": "Turn the attached release brief source into a governed skill package.", + "input_files": [ + "fixtures/release-brief-source.md" + ], + "metadata": { + "case_type": "boundary", + "tier": "governed" + }, + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0 + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0 + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0 + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0 + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "file-backed-governed-package:A", + "output": "Draft a release announcement with the changelog, support notes, and owner name." + }, + "variant_b": { + "blind_id": "file-backed-governed-package:B", + "output": "Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them." + } + } + ] + }, + "output_review_kit": { + "schema_version": "1.0", + "ok": true, + "summary": { + "case_count": 5, + "ready_for_adjudication_count": 0, + "pending_decision_count": 5, + "invalid_decision_count": 0, + "reviewer_metadata_present": false, + "answer_key_hidden": true, + "answer_key_path_exposed": false, + "ready_to_run_adjudication": false, + "failure_count": 0 + }, + "artifacts": { + "reviewer_kit_json": "reports/output_review_kit.json", + "reviewer_kit_markdown": "reports/output_review_kit.md", + "reviewer_kit_html": "reports/output_review_kit.html", + "blind_pack_json": "reports/output_blind_review_pack.json", + "blind_pack_markdown": "reports/output_blind_review_pack.md", + "decisions": "reports/output_review_decisions.json", + "adjudication_json": "reports/output_review_adjudication.json", + "adjudication_markdown": "reports/output_review_adjudication.md" + }, + "template_written": false, + "review_contract": { + "reviewer_steps": [ + "Open reports/output_blind_review_pack.md or this kit and compare Variant A vs Variant B for each case.", + "Record choices in reports/output_review_decisions.json without opening the answer key.", + "Use winner_variant A or B, confidence from 0 to 1, and a short reason for every case.", + "Set reviewer_attestation truthfully after choices are recorded and before adjudication.", + "Run python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication after choices are recorded.", + "Refresh python3 scripts/yao.py review-studio . before asking for release approval." + ], + "required_fields": { + "reviewer": "Human reviewer name or review group.", + "reviewed_at": "Review date or timestamp.", + "winner_variant": "A or B for every case.", + "confidence": "Optional numeric confidence from 0 to 1.", + "reason": "Required rationale based on the rubric, not on hidden labels.", + "reviewer_attestation": "Blind-review and answer-key-order attestation after decisions are complete.", + "review_integrity": "Blind pack and prompt fingerprints generated by the template." + }, + "privacy_contract": [ + "The answer key is intentionally withheld from this kit.", + "Do not inspect hidden labels or expected winners before decisions are recorded.", + "Do not paste private user data into decision reasons.", + "Pending decisions must stay pending instead of being counted as human agreement." + ] + }, + "cases": [ + { + "case_id": "skill-package-contract", + "prompt": "Turn this repeated workflow into a reusable team skill package.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-entrypoint", + "description": "Output names the routeable Skill entrypoint.", + "weight": 1.0 + }, + { + "id": "has-interface", + "description": "Output includes neutral interface metadata.", + "weight": 1.0 + }, + { + "id": "has-report", + "description": "Output points reviewer to generated overview report.", + "weight": 1.0 + }, + { + "id": "has-resource-boundary", + "description": "Output preserves entrypoint/resource separation.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "skill-package-contract:A", + "output": "Create a routeable SKILL.md, agents/interface.yaml, reports/skill-overview.html, and a focused eval plan. Keep the root entrypoint lean, move durable guidance into references/, scripts into scripts/, and evidence into reports/." + }, + "variant_b": { + "blind_id": "skill-package-contract:B", + "output": "I can write a prompt for that workflow and include a short checklist." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "output-eval-expectation", + "prompt": "Upgrade this production skill so we know whether the generated output is better than baseline.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-baseline-comparison", + "description": "Output explicitly compares with-skill and baseline outputs.", + "weight": 1.0 + }, + { + "id": "has-assertion-grading", + "description": "Output includes objective assertion grading.", + "weight": 1.0 + }, + { + "id": "has-scorecard", + "description": "Output produces a scorecard report path.", + "weight": 1.0 + }, + { + "id": "has-failure-taxonomy", + "description": "Output records failure taxonomy.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "output-eval-expectation:A", + "output": "Add Output Eval Lab cases with baseline_output and with_skill_output, run assertion grading, report with-skill vs baseline pass-rate delta, and record failure taxonomy plus next fixes in reports/output_quality_scorecard.md." + }, + "variant_b": { + "blind_id": "output-eval-expectation:B", + "output": "Add more examples and run the trigger eval again." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "ir-before-packaging", + "prompt": "Package this skill for OpenAI, Claude, Agent Skills, and generic targets.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "has-ir-first", + "description": "Output requires Skill IR before packaging.", + "weight": 1.0 + }, + { + "id": "has-core-ir-fields", + "description": "Output lists core IR contract fields.", + "weight": 1.0 + }, + { + "id": "has-targets", + "description": "Output names requested runtime targets.", + "weight": 1.0 + }, + { + "id": "has-semantic-preservation", + "description": "Output says targets must preserve the capability contract.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "ir-before-packaging:A", + "output": "Export folders for each platform and adjust files as needed." + }, + "variant_b": { + "blind_id": "ir-before-packaging:B", + "output": "Export Skill IR first with job_to_be_done, trigger_surface, workflow, resources, eval_plan, risk, and governance. Then compile or package targets from the IR so OpenAI, Claude, Agent Skills, and generic packages preserve the same capability contract." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "near-neighbor-boundary", + "prompt": "I only need a one-off summary of these notes, not a reusable process.", + "input_files": [], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "declines-skill", + "description": "Output refuses unnecessary skill creation for one-off work.", + "weight": 1.0 + }, + { + "id": "names-near-neighbor", + "description": "Output labels the request as near-neighbor instead of owned work.", + "weight": 1.0 + }, + { + "id": "requires-reuse-signal", + "description": "Output asks for repeat-use evidence before packaging.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "near-neighbor-boundary:A", + "output": "Create a SKILL.md and a reusable workflow anyway so future notes can use it." + }, + "variant_b": { + "blind_id": "near-neighbor-boundary:B", + "output": "Do not create a skill for this one-off request. Treat it as a near-neighbor: answer the summary directly unless the user confirms repeated use, shared ownership, or a reusable output contract." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + }, + { + "case_id": "file-backed-governed-package", + "prompt": "Turn the attached release brief source into a governed skill package.", + "input_files": [ + "fixtures/release-brief-source.md" + ], + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": [ + { + "id": "uses-file-backed-evidence", + "description": "Output names file-backed source evidence.", + "weight": 1.0 + }, + { + "id": "has-governance", + "description": "Output preserves governed ownership metadata.", + "weight": 1.0 + }, + { + "id": "has-output-contract", + "description": "Output preserves output and rollback boundaries.", + "weight": 1.0 + }, + { + "id": "has-trust-and-scorecard", + "description": "Output requires trust and output scorecard artifacts.", + "weight": 1.0 + }, + { + "id": "does-not-invent-evidence", + "description": "Output forbids invented launch evidence.", + "weight": 1.0 + } + ], + "variant_a": { + "blind_id": "file-backed-governed-package:A", + "output": "Draft a release announcement with the changelog, support notes, and owner name." + }, + "variant_b": { + "blind_id": "file-backed-governed-package:B", + "output": "Use the file-backed fixture as source evidence, then create a governed skill package with SKILL.md, agents/interface.yaml, owner, review cadence, input_files, output contract, rollback boundary, trust report, and reports/output_quality_scorecard.md. Mark missing launch metrics as missing evidence instead of inventing them." + }, + "decision_state": { + "status": "awaiting-decision", + "winner_variant_recorded": false, + "confidence_recorded": false, + "reason_recorded": false, + "blocking_reason": "Decision template exists but this row is still blank." + } + } + ], + "failures": [] + }, + "output_review_adjudication": { + "schema_version": "1.0", + "ok": true, + "summary": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "agreement_count": 0, + "disagreement_count": 0, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "pending_answer_hidden_count": 5, + "agreement_rate": null, + "needs_review": true, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "failure_count": 0, + "reviewer_checklist_count": 5, + "reviewer_checklist_pending_count": 5, + "reviewer_checklist_invalid_count": 0, + "reviewer_checklist_ready_count": 0 + }, + "reviewer": "", + "reviewed_at": "", + "review_integrity": { + "blind_pack_sha256": "788604c67fa31178c9bba06a52731adcbee9c1e72c00767cf7950643812a6e5c", + "case_count": 5, + "case_ids": [ + "skill-package-contract", + "output-eval-expectation", + "ir-before-packaging", + "near-neighbor-boundary", + "file-backed-governed-package" + ], + "prompt_sha256_by_case": { + "skill-package-contract": "95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5", + "output-eval-expectation": "3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4", + "ir-before-packaging": "d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80", + "near-neighbor-boundary": "2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0", + "file-backed-governed-package": "c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d" + } + }, + "reviewer_attestation": { + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded": true, + "reviewer_reason_required": true + }, + "artifacts": { + "blind_pack": "reports/output_blind_review_pack.json", + "answer_key": "reports/output_blind_answer_key.json", + "decisions": "reports/output_review_decisions.json", + "json": "reports/output_review_adjudication.json", + "markdown": "reports/output_review_adjudication.md" + }, + "template_written": false, + "pairs": [ + { + "case_id": "skill-package-contract", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5" + }, + { + "case_id": "output-eval-expectation", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4" + }, + { + "case_id": "ir-before-packaging", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80" + }, + { + "case_id": "near-neighbor-boundary", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0" + }, + { + "case_id": "file-backed-governed-package", + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": false, + "reviewer_winner_variant": "", + "confidence": null, + "reason": "", + "prompt_sha256": "c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d" + } + ], + "reviewer_checklist": [ + { + "case_id": "skill-package-contract", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "95405d805bd516d0998b69d31ece2c1a465b0aa99f04d603628a3939028133b5", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "output-eval-expectation", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "3829e97814ca269ad8e400ec984c56ddcd54b79448d3846c15e7f5ff44c2edc4", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "ir-before-packaging", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "d00333fa41f2e28dc7505f25f259aefb82e65b2df1abda2822686c9a6f07bb80", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "near-neighbor-boundary", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "2d0585e9fa6d03e28786f5e8f0c090781eb209afd344d25e2f91203c49a656d0", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + }, + { + "case_id": "file-backed-governed-package", + "readiness": "awaiting-decision", + "blocking_reason": "Reviewer has not selected A or B yet; answer key remains hidden.", + "status": "pending", + "reviewer_winner_variant": "", + "answer_key_visible": false, + "prompt_sha256": "c79a554b573517682da48879c1ef54399841d9ebf570088a2174dc38de39cc7d", + "blind_pack_path": "reports/output_blind_review_pack.json", + "decisions_path": "reports/output_review_decisions.json", + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio ." + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded." + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide." + ] + } + ], + "failures": [] + }, + "benchmark_reproducibility": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "git_status": { + "available": true, + "dirty": true, + "changed_file_count": 37, + "generated_dirty": true, + "generated_changed_file_count": 36, + "source_dirty": true, + "source_changed_file_count": 1, + "sample": [ + " M SKILL.md", + " M registry/index.json", + " M registry/packages/yao-meta-skill.json", + " M reports/adaptation_proposals.json", + " M reports/adaptation_proposals.md", + " M reports/adoption_drift_report.json", + " M reports/benchmark_reproducibility.json", + " M reports/benchmark_reproducibility.md", + " M reports/compiled_targets.json", + " M reports/context_budget.json", + " M reports/context_budget.md", + " M reports/context_budget_summary.json" + ], + "source_sample": [ + " M SKILL.md" + ], + "generated_sample": [ + " M registry/index.json", + " M registry/packages/yao-meta-skill.json", + " M reports/adaptation_proposals.json", + " M reports/adaptation_proposals.md", + " M reports/adoption_drift_report.json", + " M reports/benchmark_reproducibility.json", + " M reports/benchmark_reproducibility.md", + " M reports/compiled_targets.json", + " M reports/context_budget.json", + " M reports/context_budget.md", + " M reports/context_budget_summary.json", + " M reports/evidence_consistency.json" + ], + "generated_dirty_prefixes": [ + "dist/", + "registry/index.json", + "registry/packages/", + "reports/", + "skill_atlas/", + "skill-ir/examples/" + ], + "scope": "generation-time status before this report is written" + }, + "summary": { + "reproducibility_ready": true, + "release_lock_ready": false, + "methodology_complete": true, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "evidence_bundle_sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158", + "source_contract_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "output_case_count": 5, + "failure_disclosure_count": 3, + "command_count": 23, + "command_executed_count": 10, + "timing_observed_count": 10, + "model_executed_count": 10, + "token_observed_count": 10, + "human_review_complete": false, + "provider_evidence_complete": true, + "world_class_ready": false, + "world_class_open_gap_count": 3, + "world_class_task_count": 4, + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4, + "working_tree_dirty": true, + "changed_file_count": 37, + "source_tree_dirty": true, + "source_changed_file_count": 1, + "generated_tree_dirty": true, + "generated_changed_file_count": 36 + }, + "beta_test_release": { + "ready": false, + "scope": "beta/public test release without superiority, fully-reviewed, or world-class claims", + "blockers": [ + "release lock is not clean or commit is unavailable" + ], + "allowed_deferred_evidence": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "reason": "Provider-backed source evidence exists, but formal ledger submission and reviewer acceptance are still pending before public claims." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "reason": "Human adjudication evidence is still pending; deferred for beta/public testing and still required before superiority, fully-reviewed, or world-class claims." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "reason": "Native enforcement proof is still pending; deferred for beta/public testing and still required before world-class claims." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "reason": "Real client telemetry is still pending; deferred for beta/public testing and still required before world-class claims." + } + ], + "policy": "Human blind-review, native permission enforcement, real client telemetry, and ledger acceptance may be deferred for beta/public testing, but public claims must remain blocked until those evidence entries are accepted.", + "required_wording": "Use beta, public test, or technical preview wording; do not claim world-class readiness, fully reviewed quality, or proven superiority over baseline." + }, + "public_claim": { + "ready": false, + "scope": "public benchmark or world-class readiness claim", + "blockers": [ + "release lock is not clean or commit is unavailable", + "human blind-review adjudication is incomplete", + "world-class evidence is not accepted yet (3 open gaps, 4 ledger pending)", + "world-class source checks are not all accepted (12/19 pass, 7 blocked)" + ], + "policy": "Local reproducibility can pass before public claims; public claims require provider evidence, human adjudication, clean release lock, accepted world-class evidence, and complete source checks." + }, + "release_lock": { + "ready": false, + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "status_scope": "generation-time status before this report is written", + "source_changed_file_count": 1, + "generated_changed_file_count": 36, + "reason": "source files were dirty at generation time" + }, + "evidence_bundle": { + "algorithm": "sha256(path,label,exists,artifact_sha256)", + "artifact_count": 25, + "existing_count": 25, + "missing_count": 0, + "missing_paths": [], + "sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158" + }, + "methodology": { + "path": "reports/benchmark_methodology.md", + "exists": true, + "sections": [ + { + "heading": "## Benchmark Types", + "exists": true + }, + { + "heading": "## Sample Sources", + "exists": true + }, + { + "heading": "## Evaluation Dimensions", + "exists": true + }, + { + "heading": "## Weighting Rule", + "exists": true + }, + { + "heading": "## Failure Disclosure", + "exists": true + }, + { + "heading": "## Reproduction", + "exists": true + } + ], + "missing_sections": [] + }, + "artifacts_checked": [ + { + "label": "methodology", + "path": "reports/benchmark_methodology.md", + "exists": true, + "bytes": 2715, + "sha256": "57025e0123ce5d10401c5bff376d2eeeac7943c83897ae1ad3fb22cadf790f92" + }, + { + "label": "failure_disclosure", + "path": "evals/failure-cases.md", + "exists": true, + "bytes": 889, + "sha256": "28833c0d4a217d612879d193fb5de199880dd5b3093ab5757e4315600fa4fb08" + }, + { + "label": "output_cases", + "path": "evals/output/cases.jsonl", + "exists": true, + "bytes": 6555, + "sha256": "a6ae9685711620d7203b73ace4412194ae287689f945b5139ec8ad75b9eefe04" + }, + { + "label": "output_schema", + "path": "evals/output/schema.json", + "exists": true, + "bytes": 2193, + "sha256": "8ee340c95064260c5e952be614e19841ac676162c6bf01d21b107e38cb04e0b9" + }, + { + "label": "output_scorecard", + "path": "reports/output_quality_scorecard.json", + "exists": true, + "bytes": 25530, + "sha256": "0806258a8e084b27e112537faff0de64a8519ca90cfdc78b57c0e4c08a514cca" + }, + { + "label": "output_execution", + "path": "reports/output_execution_runs.json", + "exists": true, + "bytes": 8449, + "sha256": "4df66b63d2e717a559a6881b966fb1b92def7329cc89aa48ee0d8596f87402e5" + }, + { + "label": "blind_review", + "path": "reports/output_blind_review_pack.json", + "exists": true, + "bytes": 7804, + "sha256": "bbe2db8ec2776fe289cd7d6bb78d48c2b8baa106ad37f79c15e43669b10c9390" + }, + { + "label": "review_adjudication", + "path": "reports/output_review_adjudication.json", + "exists": true, + "bytes": 14084, + "sha256": "91fd88dd9b0f8876f68027a87893d1852dac59772613345bbbfabba658c9227e" + }, + { + "label": "trigger_scorecard", + "path": "reports/route_scorecard.json", + "exists": true, + "bytes": 17042, + "sha256": "53fc22d220dc11453c1e66a17ec2d935e069f9bc936952fe4a53afdb4c71b2cc" + }, + { + "label": "runtime_conformance", + "path": "reports/conformance_matrix.json", + "exists": true, + "bytes": 10342, + "sha256": "97f9ba949c23a60b00e9ba2ff279ca03ba517845cc7c62aa8a42645d58006c7e" + }, + { + "label": "trust_report", + "path": "reports/security_trust_report.json", + "exists": true, + "bytes": 139090, + "sha256": "108aee597a1f245f73d9b69454b6fa8a144545f905231a7c314364c7066541c6" + }, + { + "label": "python_compatibility", + "path": "reports/python_compatibility.json", + "exists": true, + "bytes": 30497, + "sha256": "4d82942052a2ca87db7acea451096332d4b8bf6420a29fa9f5596e83464d21ea" + }, + { + "label": "registry_audit", + "path": "reports/registry_audit.json", + "exists": true, + "bytes": 3155, + "sha256": "d307675d5c6c3cd2f104ce7d963b37d36113f35334e4c6895b55fbb8b255e915" + }, + { + "label": "package_verification", + "path": "reports/package_verification.json", + "exists": true, + "bytes": 19525, + "sha256": "e2ad726ce0486be0f57d88e518ef4d265479a04756419c6f8df50cf7d05b1f14" + }, + { + "label": "install_simulation", + "path": "reports/install_simulation.json", + "exists": true, + "bytes": 8947, + "sha256": "65adf51d8c4b1d5ea49edde09004f7e4bfc06127e640baf2de23af695fbe0728" + }, + { + "label": "skill_os2_audit", + "path": "reports/skill_os2_audit.json", + "exists": true, + "bytes": 13824, + "sha256": "150c0027deaa3c2dd44db5380e9a9b16f123e8a8e3ff6142c711b2b0c432f81c" + }, + { + "label": "world_class_evidence_plan", + "path": "reports/world_class_evidence_plan.json", + "exists": true, + "bytes": 23314, + "sha256": "483ed2b3bd020fa2c5a888300c0d0c1a265eebaf648076581f082f2d527d3669" + }, + { + "label": "world_class_evidence_ledger", + "path": "reports/world_class_evidence_ledger.json", + "exists": true, + "bytes": 26274, + "sha256": "b19e7113ec463dbb1cfb87c45d3c51624151aeea1747c8d9c286b6967a385c6a" + }, + { + "label": "world_class_evidence_intake", + "path": "reports/world_class_evidence_intake.json", + "exists": true, + "bytes": 20922, + "sha256": "885bff9c1c8546c606f583a982a2283229438dbdcd44f3c7e55aff71f858f8f4" + }, + { + "label": "world_class_evidence_preflight", + "path": "reports/world_class_evidence_preflight.json", + "exists": true, + "bytes": 90854, + "sha256": "06e93b1c0c99107002466d9418a4910c82397bce26c3bbdd5b5cc9875eeb65b2" + }, + { + "label": "world_class_submission_review", + "path": "reports/world_class_submission_review.json", + "exists": true, + "bytes": 17292, + "sha256": "e8df5c72ade7ad0704b6ee5dd6aedefea98f79a9a370cd4d80655401862e87f4" + }, + { + "label": "world_class_operator_runbook", + "path": "reports/world_class_operator_runbook.json", + "exists": true, + "bytes": 83712, + "sha256": "bb67813244d64f8e5e416620bb9d7856d95d85a1ec7f9581b2a97b9fed47e02e" + }, + { + "label": "world_class_operator_runbook_markdown", + "path": "reports/world_class_operator_runbook.md", + "exists": true, + "bytes": 25504, + "sha256": "e8d4dee7541323f0e6fa6be4903e7c482b1d00ef1db5594f0c44212088b53d85" + }, + { + "label": "world_class_operator_runbook_html", + "path": "reports/world_class_operator_runbook.html", + "exists": true, + "bytes": 34651, + "sha256": "022594013b2469a0e5776e3f5f6a99fa069917c5ba5cb90256b7841b1f5fd1e3" + }, + { + "label": "world_class_claim_guard", + "path": "reports/world_class_claim_guard.json", + "exists": true, + "bytes": 20713, + "sha256": "911ba833055ab857250f88d88ccdd0bb652076a81214f725c49d763352035572" + } + ], + "missing_artifacts": [], + "reproduction_commands": [ + { + "label": "source commit", + "command": "git rev-parse HEAD", + "evidence": "git commit hash" + }, + { + "label": "trigger eval", + "command": "make eval-suite", + "evidence": "reports/eval_suite.json" + }, + { + "label": "output eval", + "command": "python3 scripts/yao.py output-eval", + "evidence": "reports/output_quality_scorecard.json" + }, + { + "label": "output execution", + "command": "python3 scripts/yao.py output-exec --runner-command '[\"python3\",\"scripts/local_output_eval_runner.py\"]'", + "evidence": "reports/output_execution_runs.json" + }, + { + "label": "blind review adjudication", + "command": "python3 scripts/yao.py output-review", + "evidence": "reports/output_review_adjudication.json" + }, + { + "label": "skill ir", + "command": "python3 scripts/yao.py skill-ir . --output-json skill-ir/examples/yao-meta-skill.json", + "evidence": "skill-ir/examples/yao-meta-skill.json" + }, + { + "label": "runtime conformance", + "command": "python3 scripts/yao.py conformance .", + "evidence": "reports/conformance_matrix.json" + }, + { + "label": "trust report", + "command": "python3 scripts/yao.py trust .", + "evidence": "reports/security_trust_report.json" + }, + { + "label": "python compatibility", + "command": "python3 scripts/yao.py python-compat .", + "evidence": "reports/python_compatibility.json" + }, + { + "label": "package", + "command": "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip", + "evidence": "dist/yao-meta-skill.zip" + }, + { + "label": "package verify", + "command": "python3 scripts/yao.py package-verify . --package-dir dist --require-zip", + "evidence": "reports/package_verification.json" + }, + { + "label": "install simulate", + "command": "python3 scripts/yao.py install-simulate . --package-dir dist", + "evidence": "reports/install_simulation.json" + }, + { + "label": "registry audit", + "command": "python3 scripts/yao.py registry-audit .", + "evidence": "reports/registry_audit.json" + }, + { + "label": "skill os audit", + "command": "python3 scripts/yao.py skill-os2-audit .", + "evidence": "reports/skill_os2_audit.json" + }, + { + "label": "world-class evidence plan", + "command": "python3 scripts/yao.py world-class-evidence .", + "evidence": "reports/world_class_evidence_plan.json" + }, + { + "label": "world-class evidence ledger", + "command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_ledger.json" + }, + { + "label": "world-class evidence intake", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_intake.json" + }, + { + "label": "world-class evidence preflight", + "command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_preflight.json" + }, + { + "label": "world-class submission review", + "command": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_submission_review.json" + }, + { + "label": "world-class operator runbook", + "command": "python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_operator_runbook.json" + }, + { + "label": "world-class claim guard", + "command": "python3 scripts/yao.py world-class-claim-guard .", + "evidence": "reports/world_class_claim_guard.json" + }, + { + "label": "evidence consistency", + "command": "python3 scripts/yao.py evidence-consistency .", + "evidence": "reports/evidence_consistency.json" + }, + { + "label": "full ci", + "command": "make ci-test", + "evidence": "CI target output" + } + ], + "failure_disclosure": { + "path": "evals/failure-cases.md", + "case_count": 3, + "policy": "Keep representative failures visible and tied to regression checks." + }, + "limitations": [ + "The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately.", + "Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet.", + "Pending blind-review decisions are visible but do not count as human adjudication.", + "World-class readiness remains false until external and human evidence gaps close.", + "Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims." + ], + "artifacts": { + "json": "reports/benchmark_reproducibility.json", + "markdown": "reports/benchmark_reproducibility.md" + } + }, + "skill_os2_coverage": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "item_count": 21, + "module_count": 8, + "recommended_pr_count": 13, + "pass_count": 21, + "warn_count": 0, + "missing_count": 0, + "extension_track_count": 4, + "extension_partial_count": 0, + "extension_planned_count": 0, + "extension_covered_count": 4, + "adaptive_extension_ready": true, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_evidence_pending_count": 4, + "decision": "local-blueprint-covered-evidence-pending" + }, + "status_counts": { + "pass": 21, + "warn": 0, + "missing": 0 + }, + "extension_status_counts": { + "covered": 4 + }, + "modules": [ + { + "key": "skill-ir", + "category": "core-module", + "label": "Skill IR", + "status": "pass", + "objective": "Platform-neutral capability contract exists before platform-specific packaging.", + "current": "schema 2.0.0; targets 5", + "command": "python3 scripts/yao.py skill-ir .", + "test": "python3 tests/verify_skill_ir.py", + "evidence": [ + { + "path": "skill-ir/schema.json", + "exists": true + }, + { + "path": "skill-ir/examples/yao-meta-skill.json", + "exists": true + }, + { + "path": "scripts/export_skill_ir.py", + "exists": true + }, + { + "path": "tests/verify_skill_ir.py", + "exists": true + } + ], + "next_action": "Keep all target packages compiled from IR rather than hand-maintained per target." + }, + { + "key": "output-eval-lab", + "category": "core-module", + "label": "Output Eval Lab", + "status": "pass", + "objective": "with-skill/baseline output quality is measured with assertions, execution evidence, and blind review packs.", + "current": "5 cases; delta 100.0; execution 10", + "command": "python3 scripts/yao.py output-exec . && python3 scripts/yao.py output-review .", + "test": "python3 tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "evals/output/schema.json", + "exists": true + }, + { + "path": "evals/output/cases.jsonl", + "exists": true + }, + { + "path": "scripts/run_output_eval.py", + "exists": true + }, + { + "path": "scripts/run_output_execution.py", + "exists": true + }, + { + "path": "reports/output_quality_scorecard.json", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Add more real-file and adversarial holdout cases as adoption data grows." + }, + { + "key": "runtime-conformance", + "category": "core-module", + "label": "Runtime Conformance", + "status": "pass", + "objective": "Target packages can be consumed by OpenAI, Claude, Agent Skills, VS Code, and generic targets.", + "current": "5/5 targets pass", + "command": "python3 scripts/yao.py conformance .", + "test": "python3 tests/verify_conformance_suite.py", + "evidence": [ + { + "path": "runtime/conformance/schema.json", + "exists": true + }, + { + "path": "scripts/run_conformance_suite.py", + "exists": true + }, + { + "path": "reports/conformance_matrix.json", + "exists": true + }, + { + "path": "tests/verify_conformance_suite.py", + "exists": true + } + ], + "next_action": "Keep conformance cases aligned with current platform metadata rules." + }, + { + "key": "trust-security", + "category": "core-module", + "label": "Trust Security", + "status": "pass", + "objective": "Scripts, dependencies, permissions, secrets, and package hash are reviewable for team distribution.", + "current": "155 scripts; secrets 0; help failures 0", + "command": "python3 scripts/yao.py trust .", + "test": "python3 tests/verify_trust_check.py", + "evidence": [ + { + "path": "scripts/trust_check.py", + "exists": true + }, + { + "path": "security/trust_policy.md", + "exists": true + }, + { + "path": "security/script_policy.md", + "exists": true + }, + { + "path": "security/permission_policy.json", + "exists": true + }, + { + "path": "reports/security_trust_report.json", + "exists": true + }, + { + "path": "tests/verify_trust_check.py", + "exists": true + } + ], + "next_action": "Keep high-permission approvals scoped, expiring, and mapped to target enforcement." + }, + { + "key": "skill-atlas", + "category": "core-module", + "label": "Skill Atlas", + "status": "pass", + "objective": "Team skill portfolio reveals route collisions, stale ownership, dependency graph, and no-route opportunities.", + "current": "11 scanned skills; actionable collisions 0", + "command": "python3 scripts/yao.py skill-atlas --workspace-root .", + "test": "python3 tests/verify_skill_atlas.py", + "evidence": [ + { + "path": "scripts/build_skill_atlas.py", + "exists": true + }, + { + "path": "skill_atlas/catalog.json", + "exists": true + }, + { + "path": "skill_atlas/route_overlap_matrix.csv", + "exists": true + }, + { + "path": "skill_atlas/dependency_graph.json", + "exists": true + }, + { + "path": "reports/skill_atlas.json", + "exists": true + }, + { + "path": "tests/verify_skill_atlas.py", + "exists": true + } + ], + "next_action": "Use real telemetry to rank stale or drifting skills by impact, not only by static metadata." + }, + { + "key": "registry-distribution", + "category": "core-module", + "label": "Registry Distribution", + "status": "pass", + "objective": "Skill packages are installable, versioned, checksumed, and upgrade-reviewable.", + "current": "archive entries 580; install failures 0", + "command": "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip && python3 scripts/yao.py registry-audit .", + "test": "python3 tests/verify_registry_audit.py", + "evidence": [ + { + "path": "registry/index.schema.json", + "exists": true + }, + { + "path": "registry/package.schema.json", + "exists": true + }, + { + "path": "registry/packages/yao-meta-skill.json", + "exists": true + }, + { + "path": "scripts/registry_audit.py", + "exists": true + }, + { + "path": "reports/package_verification.json", + "exists": true + }, + { + "path": "reports/install_simulation.json", + "exists": true + }, + { + "path": "tests/verify_registry_audit.py", + "exists": true + } + ], + "next_action": "Regenerate registry metadata after package verification so source and archive checksums stay aligned." + }, + { + "key": "review-studio", + "category": "core-module", + "label": "Review Studio", + "status": "pass", + "objective": "One HTML page supports first-pass production review across trigger, output, runtime, trust, release, and evidence actions.", + "current": "16 gates; decision review; warnings 3", + "command": "python3 scripts/yao.py review-studio .", + "test": "python3 tests/verify_review_studio.py", + "evidence": [ + { + "path": "scripts/render_review_studio.py", + "exists": true + }, + { + "path": "reports/review-studio.html", + "exists": true + }, + { + "path": "reports/review-studio.json", + "exists": true + }, + { + "path": "tests/verify_review_studio.py", + "exists": true + } + ], + "next_action": "Close pending human and external evidence before claiming full release readiness." + }, + { + "key": "telemetry-drift", + "category": "core-module", + "label": "Telemetry Drift", + "status": "pass", + "objective": "Real usage feedback is captured as metadata-only local-first drift signals.", + "current": "events 1; recipes 5; risk low", + "command": "python3 scripts/yao.py telemetry-hooks . && python3 scripts/yao.py adoption-drift .", + "test": "python3 tests/verify_telemetry_hooks.py", + "evidence": [ + { + "path": "scripts/emit_telemetry_event.py", + "exists": true + }, + { + "path": "scripts/import_telemetry_events.py", + "exists": true + }, + { + "path": "scripts/telemetry_native_host.py", + "exists": true + }, + { + "path": "reports/adoption_drift_report.json", + "exists": true + }, + { + "path": "reports/telemetry_hook_recipes.json", + "exists": true + }, + { + "path": "tests/verify_telemetry_hooks.py", + "exists": true + } + ], + "next_action": "Install a real client and import production metadata-only events into the local drift loop." + } + ], + "recommended_prs": [ + { + "key": "benchmark-methodology", + "category": "recommended-pr", + "label": "Benchmark Methodology", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "25 required artifacts checked", + "command": "make ci-test", + "test": "tests/verify_benchmark_reproducibility.py", + "evidence": [ + { + "path": "reports/benchmark_methodology.md", + "exists": true + }, + { + "path": "reports/benchmark_reproducibility.json", + "exists": true + }, + { + "path": "tests/verify_benchmark_reproducibility.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "output-eval-schema", + "category": "recommended-pr", + "label": "Output Eval Schema", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "5 output cases", + "command": "make ci-test", + "test": "tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "evals/output/schema.json", + "exists": true + }, + { + "path": "evals/output/cases.jsonl", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "output-eval-runner", + "category": "recommended-pr", + "label": "Output Eval Runner", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "delta 100.0", + "command": "make ci-test", + "test": "tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "scripts/run_output_eval.py", + "exists": true + }, + { + "path": "reports/output_quality_scorecard.json", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "output-quality-scorecard", + "category": "recommended-pr", + "label": "Output Quality Scorecard", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "gate pass True", + "command": "make ci-test", + "test": "tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "reports/output_quality_scorecard.md", + "exists": true + }, + { + "path": "reports/output_quality_scorecard.json", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "skill-ir-v0", + "category": "recommended-pr", + "label": "Skill IR V0", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "schema 2.0.0", + "command": "make ci-test", + "test": "tests/verify_skill_ir.py", + "evidence": [ + { + "path": "skill-ir/schema.json", + "exists": true + }, + { + "path": "skill-ir/examples/yao-meta-skill.json", + "exists": true + }, + { + "path": "tests/verify_skill_ir.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "compiler-refactor", + "category": "recommended-pr", + "label": "Compiler Refactor", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "5/5 compiled targets", + "command": "make ci-test", + "test": "tests/verify_compile_skill.py", + "evidence": [ + { + "path": "scripts/compile_skill.py", + "exists": true + }, + { + "path": "reports/compiled_targets.json", + "exists": true + }, + { + "path": "tests/verify_compile_skill.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "agent-skills-conformance", + "category": "recommended-pr", + "label": "Agent Skills Conformance", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "agent-skills target present", + "command": "make ci-test", + "test": "tests/verify_conformance_suite.py", + "evidence": [ + { + "path": "runtime/conformance/schema.json", + "exists": true + }, + { + "path": "reports/conformance_matrix.json", + "exists": true + }, + { + "path": "tests/verify_conformance_suite.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "trust-check", + "category": "recommended-pr", + "label": "Trust Check", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "secret findings 0", + "command": "make ci-test", + "test": "tests/verify_trust_check.py", + "evidence": [ + { + "path": "scripts/trust_check.py", + "exists": true + }, + { + "path": "reports/security_trust_report.json", + "exists": true + }, + { + "path": "tests/verify_trust_check.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "skill-atlas-generator", + "category": "recommended-pr", + "label": "Skill Atlas Generator", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "11 scanned skills", + "command": "make ci-test", + "test": "tests/verify_skill_atlas.py", + "evidence": [ + { + "path": "scripts/build_skill_atlas.py", + "exists": true + }, + { + "path": "reports/skill_atlas.json", + "exists": true + }, + { + "path": "tests/verify_skill_atlas.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "registry-package-format", + "category": "recommended-pr", + "label": "Registry Package Format", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "registry ok True", + "command": "make ci-test", + "test": "tests/verify_registry_audit.py", + "evidence": [ + { + "path": "registry/package.schema.json", + "exists": true + }, + { + "path": "reports/registry_audit.json", + "exists": true + }, + { + "path": "tests/verify_registry_audit.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "review-studio-2", + "category": "recommended-pr", + "label": "Review Studio 2.0", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "16 review gates", + "command": "make ci-test", + "test": "tests/verify_review_studio.py", + "evidence": [ + { + "path": "scripts/render_review_studio.py", + "exists": true + }, + { + "path": "reports/review-studio.html", + "exists": true + }, + { + "path": "tests/verify_review_studio.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "migration-v2-docs", + "category": "recommended-pr", + "label": "Migration V2 Docs", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "migration guide present", + "command": "make ci-test", + "test": "docs review", + "evidence": [ + { + "path": "docs/migration-v2.md", + "exists": true + }, + { + "path": "reports/skill-os-2-review.md", + "exists": true + }, + { + "path": "README.md", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "evidence-consistency", + "category": "recommended-pr", + "label": "Evidence Consistency", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "39 consistency checks", + "command": "make ci-test", + "test": "tests/verify_evidence_consistency.py", + "evidence": [ + { + "path": "scripts/render_evidence_consistency.py", + "exists": true + }, + { + "path": "reports/evidence_consistency.json", + "exists": true + }, + { + "path": "tests/verify_evidence_consistency.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + } + ], + "reference_extension_tracks": [ + { + "key": "skill-interpretation-report", + "label": "Skill Interpretation Report", + "status": "covered", + "objective": "User-facing deep interpretation report explains use cases, triggers, inputs, outputs, workflow, principles, boundaries, quality gates, examples, and next iterations.", + "current": "Skill Overview v2 is canonical and mirrored as first-class skill-interpretation HTML/JSON with schema and tests.", + "target": "Either keep skill-overview as the canonical interpretation report with an explicit contract, or split a dedicated reports/skill-interpretation.* renderer and tests.", + "evidence": [ + { + "path": "reports/skill-overview.html", + "exists": true + }, + { + "path": "reports/skill-overview.json", + "exists": true + }, + { + "path": "scripts/render_skill_overview.py", + "exists": true + }, + { + "path": "scripts/render_skill_interpretation.py", + "exists": true + }, + { + "path": "schemas/skill-interpretation.schema.json", + "exists": true + }, + { + "path": "tests/verify_skill_interpretation.py", + "exists": true + } + ], + "next_action": "Keep overview and interpretation contracts in lockstep when report sections, metrics, or layout semantics change." + }, + { + "key": "adaptive-self-iteration", + "label": "Adaptive Self-Iteration", + "status": "covered", + "objective": "Local-first preference memory, repeated-signal extraction, adaptation proposals, approval, patch application, regression evidence, and rollback.", + "current": "Full adaptive loop includes proposal, approval, patch application, regression evidence, and rollback metadata.", + "target": "Proposal-only adaptation with explicit input source, redaction, allowlisted write targets, approval ledger, regression report, and rollback plan.", + "evidence": [ + { + "path": "references/autonomous-adaptation.md", + "exists": true + }, + { + "path": "references/user-memory-policy.md", + "exists": true + }, + { + "path": "schemas/adaptation-proposal.schema.json", + "exists": true + }, + { + "path": "scripts/summarize_user_signals.py", + "exists": true + }, + { + "path": "scripts/propose_adaptation.py", + "exists": true + }, + { + "path": "tests/verify_adaptation_safety.py", + "exists": true + }, + { + "path": "scripts/apply_adaptation.py", + "exists": true + }, + { + "path": "reports/adaptation_approval_ledger.json", + "exists": true + }, + { + "path": "reports/adaptation_regression_report.json", + "exists": true + }, + { + "path": "reports/user_patterns.json", + "exists": true + }, + { + "path": "reports/adaptation_proposals.json", + "exists": true + }, + { + "path": "reports/iteration-directions.md", + "exists": true + }, + { + "path": "reports/adoption_drift_report.md", + "exists": true + } + ], + "next_action": "Start with policy and read-only scan tests; do not read shell history or private logs unless the user provides an explicit source path." + }, + { + "key": "daily-skillops-report", + "label": "Daily SkillOps Report", + "status": "covered", + "objective": "Daily operations layer summarizes explicit-source conversation patterns, proposal-only adaptation work, approval state, release locks, and world-class evidence gaps.", + "current": "Daily SkillOps report is a CLI-backed, explicit-source operations cockpit with tests and committed dated evidence.", + "target": "Generate reports/skillops/daily/YYYY-MM-DD.* from explicit sources without private log scanning, source writes, auto-patching, or world-class overclaiming.", + "evidence": [ + { + "path": "scripts/render_daily_skillops_report.py", + "exists": true + }, + { + "path": "tests/verify_daily_skillops.py", + "exists": true + }, + { + "path": "reports/skillops/daily/2026-07-02.json", + "exists": true + }, + { + "path": "reports/skillops/daily/2026-07-02.md", + "exists": true + } + ], + "next_action": "Keep Daily SkillOps report aligned with proposal, approval, coverage, and world-class ledger contracts as the operations layer evolves." + }, + { + "key": "weekly-curator-report", + "label": "Weekly Curator Report", + "status": "covered", + "objective": "Weekly curator layer aggregates Daily SkillOps opportunities, Skill Atlas portfolio signals, release locks, and world-class evidence gaps into a maintenance queue.", + "current": "Weekly curator report is CLI-backed, proposal-only, and backed by committed weekly JSON/Markdown evidence.", + "target": "Generate reports/skillops/weekly/YYYY-WNN.* without private log scanning, source writes, auto-patching, or world-class overclaiming.", + "evidence": [ + { + "path": "scripts/render_weekly_curator_report.py", + "exists": true + }, + { + "path": "tests/verify_weekly_curator.py", + "exists": true + }, + { + "path": "reports/skillops/weekly/2026-W27.json", + "exists": true + }, + { + "path": "reports/skillops/weekly/2026-W27.md", + "exists": true + } + ], + "next_action": "Use weekly curator output as the Skill Librarian maintenance queue before approving any durable skill-library changes." + } + ], + "next_highest_leverage": [ + "Close the four world-class evidence ledger entries with accepted human or external evidence.", + "Keep the first-class skill interpretation report and Skill Overview v2 contract synchronized as the report model evolves.", + "Use Daily SkillOps with explicit curated sources to review adaptation proposals without scanning private logs or auto-patching.", + "Keep the blueprint coverage report in CI so 2.0 plan drift is visible." + ], + "source_blueprint": { + "title": "Skill Overview / Skill OS 2.0 upgrade plan", + "core_module_count": 8, + "recommended_pr_count": 13, + "reference_extension_count": 4, + "reference_extensions": [ + "Skill interpretation report", + "Adaptive self-iteration with local preference memory and approval gates", + "Daily SkillOps operations report" + ] + }, + "artifacts": { + "json": "reports/skill_os2_coverage.json", + "markdown": "reports/skill_os2_coverage.md" + } + }, + "compiled_targets": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-13", + "skill_dir": ".", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "schema_version": "1.0", + "target": "openai", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "openai", + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "degradation_strategy": "metadata-adapter", + "target_runtime": { + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "openai", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "openai", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation": { + "policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ] + }, + "scripts": { + "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/openai/agents/openai.yaml", + "targets/openai/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + "client-native script permission prompts are represented as permission contract metadata" + ] + }, + "install_scope": "plugin or skill package consumer" + }, + "target_transform": { + "target": "openai", + "adapter_mode": "metadata-adapter", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "metadata-adapter", + "permission_representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation_policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "resource_strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "script_strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "permission_enforcement": "metadata-only" + }, + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "claude", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "claude", + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "degradation_strategy": "neutral-source-plus-adapter", + "target_runtime": { + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Claude-compatible neutral source folder with adapter notes" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "claude", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "claude", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation": { + "policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ] + }, + "scripts": { + "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "metadata-fallback", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/claude/README.md", + "targets/claude/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + "vendor-native metadata fields are carried as adapter JSON and README notes" + ] + }, + "install_scope": "user or project skill directory" + }, + "target_transform": { + "target": "claude", + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source-plus-adapter", + "permission_representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation_policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "resource_strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "script_strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "permission_enforcement": "metadata-fallback" + }, + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "generic", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "generic", + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills compatible neutral package" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "generic", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "representation": "targets/generic/adapter.json::target_permission_contract", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "generic", + "native_surface": "Agent Skills compatible neutral package", + "activation": { + "policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/generic/adapter.json" + ] + }, + "scripts": { + "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "targets/generic/adapter.json", + "reports/review-studio.html" + ], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [] + }, + "install_scope": "generic Agent Skills compatible root" + }, + "target_transform": { + "target": "generic", + "adapter_mode": "agent-skills-compatible", + "generated_files": [ + "targets/generic/adapter.json" + ], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy" + }, + "preserved_semantics": [ + "trigger", + "workflow-counts", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "targets/generic/adapter.json::target_permission_contract", + "native_surface": "Agent Skills compatible neutral package", + "activation_policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "resource_strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "script_strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "agent-skills-compatible", + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "degradation_strategy": "neutral-source", + "target_runtime": { + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "Agent Skills standard source tree" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "permission_model": "metadata-only", + "native_enforcement": false, + "representation": "adapter metadata", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "agent-skills-compatible", + "native_surface": "Agent Skills standard source tree", + "activation": { + "policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Keep optional directories as relative resources next to SKILL.md.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ] + }, + "scripts": { + "strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "consumer-enforced-or-metadata-only", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [] + }, + "install_scope": "Agent Skills source root" + }, + "target_transform": { + "target": "agent-skills-compatible", + "adapter_mode": "neutral-agent-skills-source", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "neutral-source", + "permission_representation": "adapter metadata", + "native_surface": "Agent Skills standard source tree", + "activation_policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "resource_strategy": "Keep optional directories as relative resources next to SKILL.md.", + "script_strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "permission_enforcement": "consumer-enforced-or-metadata-only" + }, + "unsupported_features": [], + "warnings": [], + "failures": [] + }, + { + "schema_version": "1.0", + "target": "vscode", + "status": "pass", + "compiler": { + "name": "yao-skill-ir-compiler", + "schema_version": "1.0", + "source": "skill-ir", + "ir_source": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0" + }, + "source": { + "skill_dir": ".", + "canonical_metadata": "agents/interface.yaml", + "declared_targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "compiled_contract": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ], + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + } + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill", + "counts": { + "trigger": 27, + "output": 3, + "adversarial": 1, + "baseline": 1 + } + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "runtime": { + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "canonical_format": "agent-skills" + }, + "permissions": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "source_files_count": 6, + "target": "vscode", + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "target_runtime": { + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "native_surface": "VS Code/Copilot Agent Skills project or user scope" + } + }, + "permission_contract": { + "schema_version": "1.0", + "source": "reports/security_trust_report.json", + "source_available": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true, + "capabilities": { + "network": { + "required": true, + "script_count": 3, + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "review_reason": "Outbound hosts must match security/network_policy.json." + }, + "file_write": { + "required": true, + "script_count": 74, + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "review_reason": "Scripts write local files or generated artifacts." + }, + "subprocess": { + "required": true, + "script_count": 13, + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "review_reason": "Scripts spawn local commands and need operator review." + }, + "interactive": { + "required": false, + "script_count": 0, + "scripts": [], + "review_reason": "" + } + }, + "network_policy": { + "source": "security/network_policy.json", + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [] + }, + "help_smoke": { + "enabled": true, + "checked_count": 86, + "failed_count": 0, + "failed_scripts": [] + }, + "trust_summary": { + "secret_findings": 0, + "network_script_count": 3, + "file_write_script_count": 74, + "subprocess_script_count": 13, + "interactive_script_count": 0, + "help_smoke_failed_count": 0 + } + }, + "target_permission_contract": { + "schema_version": "1.0", + "target": "vscode", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "review_required": true, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "capability_counts": { + "network": 3, + "file_write": 74, + "subprocess": 13, + "interactive": 0 + }, + "evidence": "reports/security_trust_report.json", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks." + }, + "target_native_contract": { + "schema_version": "1.0", + "target": "vscode", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation": { + "policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "trigger_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "manual_activation_supported": true, + "automatic_activation_note": "Depends on the target client route/catalog implementation." + }, + "resources": { + "strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "counts": { + "references": 35, + "scripts": 155, + "assets": 6, + "reports": 43 + }, + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ] + }, + "scripts": { + "strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "script_count": 155, + "help_smoke_failed_count": 0 + }, + "permissions": { + "enforcement": "client-or-workspace-trust", + "native_enforcement": false, + "declared_capabilities": [ + "network", + "file_write", + "subprocess" + ], + "review_required": true + }, + "review": { + "artifacts": [ + "SKILL.md", + "agents/interface.yaml", + "reports/review-studio.html" + ], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + "VS Code installation scope is documented but not installed by this compiler" + ] + }, + "install_scope": "VS Code user or project skills directory" + }, + "target_transform": { + "target": "vscode", + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md" + }, + "preserved_semantics": [ + "trigger", + "workflow", + "resources", + "eval-plan", + "risk", + "governance", + "runtime", + "trust", + "permissions" + ], + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "permission_representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation_policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "resource_strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "script_strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "permission_enforcement": "client-or-workspace-trust" + }, + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [], + "failures": [] + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "json": "reports/compiled_targets.json", + "markdown": "reports/compiled_targets.md" + } + }, + "conformance": { + "ok": true, + "skill": "yao-meta-skill", + "targets": [ + { + "target": "openai", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares openai", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "adapter target declares openai", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "openai degradation note exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares claude", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "adapter target declares claude", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "claude degradation note exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [] + }, + { + "target": "agent-skills", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "package identity derives from skill name", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares agent-skills via agent-skills-compatible", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "canonical format is agent-skills", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [ + "agent-skills uses canonical Agent Skills metadata; provider-native execution transforms are not implemented in v0." + ] + }, + { + "target": "vscode", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "package identity derives from skill name", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares vscode", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "canonical format is agent-skills", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [ + "vscode uses canonical Agent Skills metadata; provider-native execution transforms are not implemented in v0." + ] + }, + { + "target": "generic", + "status": "pass", + "checks": [ + "SKILL.md exists", + "frontmatter name exists", + "frontmatter description exists", + "description length <= 1024", + "name is runtime-safe", + "manifest.name exists", + "manifest.version exists", + "manifest.owner exists", + "manifest.status exists", + "manifest.maturity_tier exists", + "manifest.review_cadence exists", + "manifest declares generic", + "interface.display_name exists", + "interface.short_description exists", + "interface.default_prompt exists", + "adapter target declares generic", + "activation mode exists", + "execution context exists", + "execution shell exists", + "trust source tier exists", + "generic degradation note exists", + "Skill IR schema_version is 2.0.0", + "Skill IR name matches frontmatter", + "Skill IR description matches frontmatter", + "references resource resolves: references/artifact-design-doctrine.md", + "references resource resolves: references/authoring-discipline.md", + "references resource resolves: references/autonomous-adaptation.md", + "references resource resolves: references/distribution-registry-method.md", + "references resource resolves: references/eval-playbook.md", + "references resource resolves: references/gate-selection.md", + "references resource resolves: references/governance.md", + "references resource resolves: references/human-review-template.md", + "references resource resolves: references/intent-dialogue.md", + "references resource resolves: references/iteration-philosophy.md", + "references resource resolves: references/non-skill-decision-tree.md", + "references resource resolves: references/operating-modes.md" + ], + "failures": [], + "warnings": [] + } + ], + "summary": { + "target_count": 5, + "pass_count": 5, + "fail_count": 0 + }, + "artifacts": { + "json": "reports/conformance_matrix.json", + "markdown": "reports/conformance_matrix.md" + } + }, + "runtime_permissions": { + "schema_version": "1.0", + "ok": true, + "skill_dir": ".", + "package_dir": "tests/tmp_review_studio/dist", + "expected_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "summary": { + "target_count": 4, + "pass_count": 4, + "fail_count": 0, + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "residual_risk_count": 4, + "required_capability_count": 3, + "failure_count": 0, + "installer_enforcement_source_status": "present", + "installer_enforcement_target_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "installer_permission_capability_count": 3, + "world_class_native_evidence_ready": false, + "installer_enforcement_ready": true + }, + "installer_enforcement": { + "source": "reports/install_simulation.json", + "source_status": "present", + "package_dir_matches": true, + "summary": { + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "failure_count": 0 + } + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/openai/adapter.json", + "permission_model": "metadata-only", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "openai", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "openai adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "openai adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "openai adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "openai permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "openai target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "openai target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "openai target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "openai target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "openai target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "openai review_required matches whether capabilities are required" + }, + { + "key": "openai-yaml-present", + "passed": true, + "detail": "OpenAI permission metadata YAML is readable" + }, + { + "key": "openai-yaml-permissions", + "passed": true, + "detail": "OpenAI YAML permission contract mirrors expected capabilities" + }, + { + "key": "openai-yaml-native-flag", + "passed": true, + "detail": "OpenAI YAML declares native_enforcement as a boolean" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "claude", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/claude/adapter.json", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "claude", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "claude adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "claude adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "claude adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "claude permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "claude target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "claude target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "claude target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "claude target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "claude target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "claude review_required matches whether capabilities are required" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "generic", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/generic/adapter.json", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "generic", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "generic adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "generic adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "generic adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "generic permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "generic target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "generic target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "generic target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "generic target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "generic target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "generic review_required matches whether capabilities are required" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "vscode", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/vscode/adapter.json", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "vscode", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "vscode adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "vscode adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "vscode adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "vscode permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "vscode target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "vscode target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "vscode target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "vscode target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "vscode target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "vscode review_required matches whether capabilities are required" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + } + ], + "failures": [], + "artifacts": { + "json": "reports/runtime_permission_probes.json", + "markdown": "reports/runtime_permission_probes.md" + } + }, + "trust": { + "ok": true, + "skill_dir": ".", + "summary": { + "scanned_files": 247, + "script_count": 155, + "internal_module_count": 69, + "secret_findings": 0, + "dependency_files": [ + "requirements-ci.txt" + ], + "network_script_count": 3, + "network_policy_covered_count": 3, + "network_policy_missing_count": 0, + "file_write_script_count": 74, + "permission_required_count": 3, + "permission_approved_count": 3, + "permission_missing_count": 0, + "permission_invalid_count": 0, + "permission_expired_count": 0, + "help_smoke_checked_count": 86, + "help_smoke_failed_count": 0, + "interactive_script_count": 0, + "package_hash_scope": "source-contract-without-generated-reports", + "package_hash_file_count": 247, + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + }, + "failures": [], + "warnings": [], + "secrets": [], + "scripts": [ + { + "path": "scripts/adaptation_patch_safety.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared adaptation patch target parsing and target-file baseline verification helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/adaptation_report_contracts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared adaptation approval and regression report contract decoration helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/adjudicate_output_review.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/apply_adaptation.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Approval-gated adaptive patch application with dry-run, allowlist, regression, and rollback evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/benchmark_release_lock.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_benchmark_reproducibility.py to keep release-lock git status classification out of the report renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_confusion_matrix.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_skill_atlas.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_skill_atlas_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by build_skill_atlas.py to render the static Skill Atlas HTML report.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_skill_atlas_opportunities.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by build_skill_atlas.py to keep no-route opportunity detection out of the atlas CLI.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/check_update.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": true, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [ + "https://raw.githubusercontent.com/yaojingang/yao-meta-skill/main/VERSION", + "https://raw.githubusercontent.com/yaojingang/yao-meta-skill/main/manifest.json" + ], + "network_hosts": [ + "raw.githubusercontent.com" + ] + }, + { + "path": "scripts/ci_test.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/collect_feedback.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/compile_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/compile_skill_targets.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by compile_skill for target platform model and contract builders.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/context_sizer.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/create_iteration_snapshot.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/cross_packager.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/cross_packager_contracts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by cross_packager.py to keep platform contract data separate from packaging flow.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/description_optimizer_reporting.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by optimize_description.py to render description optimization reports.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/diff_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/emit_telemetry_event.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_artifact_roles.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to compare preflight and Review Studio artifact-role contracts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_core.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py for shared report loading, comparison, and Markdown rendering helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_phase_queue.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to prevent preflight and Review Studio phase-queue drift.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_release.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to verify release evidence refresh instructions.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_skill_os2_review.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to keep Skill OS 2.0 review summary drift checks out of the main consistency renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_world_class.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to isolate world-class evidence workflow consistency checks.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/export_skill_ir.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/github_benchmark_scan.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": true, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [ + "https://api.github.com" + ], + "network_hosts": [ + "api.github.com" + ] + }, + { + "path": "scripts/governance_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/html_rendering.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Used by report renderers to escape HTML while preserving meaningful falsey values.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/import_output_review_decisions.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Imports human blind A/B reviewer decisions into the canonical output-review decision file.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/import_telemetry_events.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/init_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/judge_blind_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/lint_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/local_output_eval_runner.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/optimize_description.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/output_review_privacy.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by output-review import and world-class human evidence validators to reject raw content, credential, secret, token, and answer-key fields recursively.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/prepare_output_review_kit.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/prepare_world_class_submission_kit.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Prepares editable world-class evidence intake packets without counting drafts as accepted evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/probe_runtime_permissions.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/promotion_checker.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/propose_adaptation.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Turns redacted repeated preference patterns into proposal-only adaptation plans.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/provider_output_eval_runner.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": true, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [ + "https://api.openai.com/v1/responses", + "https://api.deepseek.com/chat/completions" + ], + "network_hosts": [ + "api.deepseek.com", + "api.openai.com" + ] + }, + { + "path": "scripts/python_compat_check.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Checks repository Python source for syntax that can pass locally but fail on the supported CI interpreter.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/reference_synthesis_markdown.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_reference_synthesis.py to keep reference synthesis modeling separate from Markdown rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/registry_audit.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_adoption_drift_report.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_architecture_maintainability.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_artifact_design_profile.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_baseline_compare.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a release-facing benchmark reproducibility manifest and Markdown report.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_context_reports.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_daily_skillops_report.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a Daily SkillOps report that summarizes explicit-source patterns, proposals, approval state, and release evidence without scanning private logs or applying patches.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_description_drift_history.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_eval_dashboard.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_evidence_consistency.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a cross-report evidence consistency gate for generated Skill OS reports.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_intent_confidence.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_intent_dialogue.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_iteration_directions.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_iteration_ledger.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_output_risk_profile.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_portability_report.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_prompt_quality_profile.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_reference_scan.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_reference_synthesis.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_regression_history.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_annotations.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_studio.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_viewer.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_waivers.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_interpretation.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders the first-class skill interpretation report while reusing the Skill Overview v2 model and layout.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_os2_audit.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_os2_coverage.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a Skill OS 2.0 blueprint-to-evidence coverage audit.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_overview.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_social_preview.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [ + "http://www.w3.org/2000/svg" + ], + "network_hosts": [ + "www.w3.org" + ] + }, + { + "path": "scripts/render_system_model.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_telemetry_hook_recipes.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_weekly_curator_report.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a weekly SkillOps curator report from generated reports without scanning private logs or writing source files.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_claim_guard.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Scans public claim surfaces so world-class completion is not claimed before accepted evidence exists.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_evidence_intake.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Validates world-class human and external evidence intake packets before ledger review.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_evidence_ledger.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a machine-checkable ledger for world-class external and human evidence gaps.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_evidence_plan.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders an operator runbook for collecting pending world-class evidence without accepting evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_preflight.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a preflight checklist for collecting pending world-class evidence without accepting evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_submission_review.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a read-only review queue for world-class evidence submissions before ledger acceptance.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/resource_boundary_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_action_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by review_studio_actions.py to keep world-class evidence action cards out of generic Review Studio action wiring.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_actions.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep Review Studio action guidance and source refs out of HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_data.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to load Review Studio source reports and metric cards.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_formatting.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to format report dictionaries as audit UI panels.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_gate_contract.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by review_studio_gates.py for stable gate scoring and contract checks.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_gate_helpers.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by review_studio_gates.py to keep reusable Review Studio gate helpers out of the main gate sequence.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_gates.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep Review Studio gate evaluation separate from HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep Review Studio layout and CSS out of gate logic.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_output_review.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to render output review checklist cards.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_panels.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep small Review Studio panel renderers out of the main page composer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_skillops.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep SkillOps summary panels out of the main HTML renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_waivers.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep waiver candidate layout out of the main renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_world_class.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to render world-class evidence cards.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_viewer_data.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_viewer.py to assemble Review Viewer data before HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_conformance_suite.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_description_optimization_suite.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_eval_suite.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_output_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_output_execution.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/simulate_install.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_ir_paths.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by compiler, registry, conformance, and report scripts to locate canonical Skill IR artifacts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_os2_coverage_markdown.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_os2_coverage.py to keep coverage data assembly separate from Markdown rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_charts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to render inline SVG report charts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_i18n.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to keep bilingual report copy and fallback rules out of HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to keep overview report layout and CSS out of data rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_metrics.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to calculate overview report metrics.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_model.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to build the v2 report data model.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_sections.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to assemble overview report quality, risk, and asset sections.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_sources.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to load source files and scan overview package assets.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_world_class.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to summarize world-class evidence readiness and roadmap actions.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skillops_opportunity.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_daily_skillops_report.py to score SkillOps opportunities without writing files.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/summarize_user_signals.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Scans an explicit local source file and summarizes redacted repeated user preference signals.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/sync_local_install.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/telemetry_native_host.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/trigger_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/trust_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/trust_check_scripts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Static script inventory helpers imported by trust_check.py.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/upgrade_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/validate_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/verify_package.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_evidence_contract.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world-class evidence reports to share intake validation and artifact integrity checks.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_human_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate blind A/B human adjudication evidence from decision rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_native_permission_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate runtime permission evidence from target-level probe rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_native_telemetry_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate native client telemetry evidence from metadata event rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_operator_runbook_coordination.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Builds world-class operator coordination steps and release gates for render_world_class_operator_runbook.py.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_phase_queue.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by prepare_world_class_submission_kit.py to group repair rows into an operator execution queue.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_preflight_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_world_class_preflight.py to keep preflight HTML layout out of data assembly.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_preflight_markdown.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_world_class_preflight.py to keep Markdown layout separate from preflight data assembly.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_provider_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate provider-backed holdout execution evidence from run rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_repair_checklist.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared by submission kit generation to turn readiness blockers into actionable repair rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_source_checks.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world-class evidence reports to keep source-evidence readiness checks consistent.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_submission_kit_rendering.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared renderer for world-class submission kit Markdown and HTML artifacts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_submission_matrix.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared by submission kit rendering to summarize draft, artifact, and source-check readiness.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_adaptation_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep adaptive scan/proposal/apply command handlers outside the thin CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_config.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py for CLI target maps and side-effect-free shaping helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_create_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep skill creation and quickstart command handlers out of the CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_distribution_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep distribution and runtime gate handlers outside the thin CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_operator_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py for operator-facing install, localized docs, and PR review diagnostics.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_output_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep output evaluation and review handlers outside the thin CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep CLI parser declarations separate from command orchestration.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao_cli_parser.py to keep evidence command declarations out of the main parser module.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser_operations.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao_cli_parser.py to keep SkillOps, telemetry, and review-loop command declarations out of the main parser module.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser_operator.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao_cli_parser.py to keep operator UX command declarations out of the main parser module.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_report_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep report and evidence command handlers out of the CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_runtime.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py and command modules for shared subprocess execution and JSON payload parsing.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_telemetry.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to record opt-in metadata-only CLI run telemetry.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + } + ], + "dependencies": { + "present": [ + "requirements-ci.txt" + ], + "pinned": [ + "PyYAML==6.0.3" + ], + "unpinned": [] + }, + "network_policy": { + "present": true, + "path": "security/network_policy.json", + "schema_version": "1.0", + "network_script_count": 3, + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [], + "default_policy": { + "require_https": true, + "custom_hosts": "deny-by-default", + "review_rule": "Network-capable scripts must declare allowed_hosts and justify any explicit custom-host override." + } + }, + "help_smoke": { + "enabled": true, + "timeout_seconds": 5.0, + "candidate_count": 86, + "checked_count": 86, + "passed_count": 86, + "failed_count": 0, + "skipped_count": 69, + "failed_scripts": [], + "results": [ + { + "path": "scripts/adjudicate_output_review.py", + "command": "python3 scripts/adjudicate_output_review.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: adjudicate_output_review.py [-h] [--blind-pack BLIND_PACK]\n [--answer-key ANSWER_KEY]\n [--decisions DECISIONS]\n [--output-json OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/apply_adaptation.py", + "command": "python3 scripts/apply_adaptation.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: apply_adaptation.py [-h] [--proposal-id PROPOSAL_ID]\n [--patch-file PATCH_FILE]\n [--proposals-json PROPOSALS_JSON]\n [--approval-ledger APPROVAL_LEDGER]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/build_confusion_matrix.py", + "command": "python3 scripts/build_confusion_matrix.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: build_confusion_matrix.py [-h] [--cases CASES]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--history-snapshot-output HISTORY_", + "stderr_excerpt": "" + }, + { + "path": "scripts/build_skill_atlas.py", + "command": "python3 scripts/build_skill_atlas.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: build_skill_atlas.py [-h] [--workspace-root WORKSPACE_ROOT]\n [--output-dir OUTPUT_DIR]\n [--report-html REPORT_HTML]\n [--report-json REPORT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/check_update.py", + "command": "python3 scripts/check_update.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: check_update.py [-h] [--root ROOT] [--cache-path CACHE_PATH]\n [--version-url VERSION_URL]\n [--manifest-url MANIFEST_URL] [--timeout TIMEOUT]\n [--max-age-days MAX_AGE_D", + "stderr_excerpt": "" + }, + { + "path": "scripts/ci_test.py", + "command": "python3 scripts/ci_test.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: ci_test.py [-h] [--tail-lines TAIL_LINES] [targets ...]\n\nRun CI test targets with compact logging.\n\npositional arguments:\n targets Optional target override.\n\noptions:\n -h, --help show this help message and ", + "stderr_excerpt": "" + }, + { + "path": "scripts/collect_feedback.py", + "command": "python3 scripts/collect_feedback.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: collect_feedback.py [-h] [--note NOTE] [--rating RATING]\n [--category CATEGORY]\n [--recommended-action RECOMMENDED_ACTION]\n [--output-json OUTPUT_JSON] [--", + "stderr_excerpt": "" + }, + { + "path": "scripts/compile_skill.py", + "command": "python3 scripts/compile_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: compile_skill.py [-h] [--target TARGET] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD] [--generated-at GENERATED_AT]\n [skill_dir]\n\nCompile Skill IR into target-specific semantic con", + "stderr_excerpt": "" + }, + { + "path": "scripts/context_sizer.py", + "command": "python3 scripts/context_sizer.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: context_sizer.py [-h] [--json] skill_dir\n\nEstimate context size for a skill package.\n\npositional arguments:\n skill_dir Path to the skill directory\n\noptions:\n -h, --help show this help message and exit\n --json Emit machine-", + "stderr_excerpt": "" + }, + { + "path": "scripts/create_iteration_snapshot.py", + "command": "python3 scripts/create_iteration_snapshot.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: create_iteration_snapshot.py [-h] --target TARGET [--label LABEL]\n [--promotion-decisions PROMOTION_DECISIONS]\n [--route-scorecard ROUTE_SCORECARD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/cross_packager.py", + "command": "python3 scripts/cross_packager.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: cross_packager.py [-h] [--platform PLATFORM] [--output-dir OUTPUT_DIR]\n [--expectations EXPECTATIONS] [--zip]\n skill_dir\n\nGenerate lightweight cross-platform packaging artifacts.\n\nposit", + "stderr_excerpt": "" + }, + { + "path": "scripts/diff_eval.py", + "command": "python3 scripts/diff_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: diff_eval.py [-h] before after\n\nDiff two eval reports.\n\npositional arguments:\n before\n after\n\noptions:\n -h, --help show this help message and exit", + "stderr_excerpt": "" + }, + { + "path": "scripts/emit_telemetry_event.py", + "command": "python3 scripts/emit_telemetry_event.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: emit_telemetry_event.py [-h] [--output-jsonl OUTPUT_JSONL]\n [--event {review_event,script_run,skill_activation,skill_output}]\n [--activation-type {explicit,implicit,manual,u", + "stderr_excerpt": "" + }, + { + "path": "scripts/export_skill_ir.py", + "command": "python3 scripts/export_skill_ir.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: export_skill_ir.py [-h] [--output-json OUTPUT_JSON] [--validate-only]\n [skill_dir]\n\nExport a platform-neutral Skill IR document from a skill package.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help ", + "stderr_excerpt": "" + }, + { + "path": "scripts/github_benchmark_scan.py", + "command": "python3 scripts/github_benchmark_scan.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: github_benchmark_scan.py [-h] --query QUERY [--top-n TOP_N]\n [--fixture-dir FIXTURE_DIR]\n [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSO", + "stderr_excerpt": "" + }, + { + "path": "scripts/governance_check.py", + "command": "python3 scripts/governance_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: governance_check.py [-h] [--require-manifest] skill_dir\n\nCheck skill governance metadata and lifecycle readiness.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help show this help message and exit\n --require-manifest", + "stderr_excerpt": "" + }, + { + "path": "scripts/import_output_review_decisions.py", + "command": "python3 scripts/import_output_review_decisions.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: import_output_review_decisions.py [-h] --input INPUT\n [--format {auto,json,jsonl,csv}]\n [--blind-pack BLIND_PACK]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/import_telemetry_events.py", + "command": "python3 scripts/import_telemetry_events.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: import_telemetry_events.py [-h] --input-jsonl INPUT_JSONL\n [--events-jsonl EVENTS_JSONL]\n [--output-json OUTPUT_JSON]\n [--output-md OU", + "stderr_excerpt": "" + }, + { + "path": "scripts/init_skill.py", + "command": "python3 scripts/init_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: init_skill.py [-h] [--description DESCRIPTION] [--title TITLE]\n [--output-dir OUTPUT_DIR]\n [--mode {governed,library,production,scaffold}]\n [--archetype {governed,library,pr", + "stderr_excerpt": "" + }, + { + "path": "scripts/judge_blind_eval.py", + "command": "python3 scripts/judge_blind_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: judge_blind_eval.py [-h] [--description DESCRIPTION]\n [--description-file DESCRIPTION_FILE] --cases CASES\n [--semantic-config SEMANTIC_CONFIG]\n\nRun a rubric-based blind trigger judg", + "stderr_excerpt": "" + }, + { + "path": "scripts/lint_skill.py", + "command": "python3 scripts/lint_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: lint_skill.py [-h] skill_dir\n\nLint a skill package for basic structure issues.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help show this help message and exit", + "stderr_excerpt": "" + }, + { + "path": "scripts/local_output_eval_runner.py", + "command": "python3 scripts/local_output_eval_runner.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: local_output_eval_runner.py [-h] [--execution-kind {command,model}]\n [--provider PROVIDER] [--model MODEL]\n\nLocal deterministic output-eval runner. It executes the runner contract\nwithout claiming pr", + "stderr_excerpt": "" + }, + { + "path": "scripts/optimize_description.py", + "command": "python3 scripts/optimize_description.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: optimize_description.py [-h] --description-file DESCRIPTION_FILE\n [--baseline-description-file BASELINE_DESCRIPTION_FILE]\n --dev-cases DEV_CASES\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/prepare_output_review_kit.py", + "command": "python3 scripts/prepare_output_review_kit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: prepare_output_review_kit.py [-h] [--blind-pack-json BLIND_PACK_JSON]\n [--blind-pack-md BLIND_PACK_MD]\n [--decisions DECISIONS]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/prepare_world_class_submission_kit.py", + "command": "python3 scripts/prepare_world_class_submission_kit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: prepare_world_class_submission_kit.py [-h] [--output-dir OUTPUT_DIR]\n [--evidence-key EVIDENCE_KEY]\n [--overwrite]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/probe_runtime_permissions.py", + "command": "python3 scripts/probe_runtime_permissions.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: probe_runtime_permissions.py [-h] [--package-dir PACKAGE_DIR]\n [--target {openai,claude,generic,vscode}]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/promotion_checker.py", + "command": "python3 scripts/promotion_checker.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: promotion_checker.py [-h] [--optimization-suite OPTIMIZATION_SUITE]\n [--route-scorecard ROUTE_SCORECARD]\n [--output-json OUTPUT_JSON]\n [--output-md OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/propose_adaptation.py", + "command": "python3 scripts/propose_adaptation.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: propose_adaptation.py [-h] [--patterns-json PATTERNS_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/provider_output_eval_runner.py", + "command": "python3 scripts/provider_output_eval_runner.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: provider_output_eval_runner.py [-h] [--provider PROVIDER]\n [--base-url BASE_URL]\n [--api-format {responses,chat-completions}]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/python_compat_check.py", + "command": "python3 scripts/python_compat_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: python_compat_check.py [-h] [--path PATH]\n [--target-python TARGET_PYTHON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/registry_audit.py", + "command": "python3 scripts/registry_audit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: registry_audit.py [-h] [--registry-dir REGISTRY_DIR]\n [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [skill_dir]\n\nBuild and ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_adoption_drift_report.py", + "command": "python3 scripts/render_adoption_drift_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_adoption_drift_report.py [-h] [--events-jsonl EVENTS_JSONL]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_architecture_maintainability.py", + "command": "python3 scripts/render_architecture_maintainability.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_architecture_maintainability.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--warn-lines WARN_LINES]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_artifact_design_profile.py", + "command": "python3 scripts/render_artifact_design_profile.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_artifact_design_profile.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender artifact design direction and visual qua", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_baseline_compare.py", + "command": "python3 scripts/render_baseline_compare.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_baseline_compare.py [-h] --entry ENTRY\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n\nRender a baseline comparison report from description optimization ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "command": "python3 scripts/render_benchmark_reproducibility.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_benchmark_reproducibility.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_context_reports.py", + "command": "python3 scripts/render_context_reports.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_context_reports.py [-h] [--generated-at GENERATED_AT]\n\nRender context budget reports for root and example skills.\n\noptions:\n -h, --help show this help message and exit\n --generated-at GENERATED_AT\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_daily_skillops_report.py", + "command": "python3 scripts/render_daily_skillops_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_daily_skillops_report.py [-h] [--source SOURCE]\n [--patterns-json PATTERNS_JSON]\n [--proposals-json PROPOSALS_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_description_drift_history.py", + "command": "python3 scripts/render_description_drift_history.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_description_drift_history.py [-h] [--history-dir HISTORY_DIR]\n [--output OUTPUT]\n\nRender description optimization drift history.\n\noptions:\n -h, --help show this help messag", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_eval_dashboard.py", + "command": "python3 scripts/render_eval_dashboard.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_eval_dashboard.py [-h] [--eval-dir EVAL_DIR]\n [--description-file DESCRIPTION_FILE]\n [--baseline-description-file BASELINE_DESCRIPTION_FILE]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_evidence_consistency.py", + "command": "python3 scripts/render_evidence_consistency.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_evidence_consistency.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_intent_confidence.py", + "command": "python3 scripts/render_intent_confidence.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_intent_confidence.py [-h] [--context-json CONTEXT_JSON]\n [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\n", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_intent_dialogue.py", + "command": "python3 scripts/render_intent_dialogue.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_intent_dialogue.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender an intent dialogue guide for a skill package.\n\npositional argume", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_iteration_directions.py", + "command": "python3 scripts/render_iteration_directions.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_iteration_directions.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender the top three next iteration directions for a ski", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_iteration_ledger.py", + "command": "python3 scripts/render_iteration_ledger.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_iteration_ledger.py [-h] [--history-dir HISTORY_DIR]\n [--description-history-dir DESCRIPTION_HISTORY_DIR]\n [--route-scorecard ROUTE_SCORECARD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_output_risk_profile.py", + "command": "python3 scripts/render_output_risk_profile.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_output_risk_profile.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender predicted output failure modes and quality constrain", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_portability_report.py", + "command": "python3 scripts/render_portability_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_portability_report.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [skill_dir]\n\nRender a portability score from neutral metadata, contracts, a", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_prompt_quality_profile.py", + "command": "python3 scripts/render_prompt_quality_profile.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_prompt_quality_profile.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender prompt behavior quality, RTF mapping, and n", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_reference_scan.py", + "command": "python3 scripts/render_reference_scan.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_reference_scan.py [-h] [--external-reference EXTERNAL_REFERENCE]\n [--user-reference USER_REFERENCE]\n [--local-constraint LOCAL_CONSTRAINT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_reference_synthesis.py", + "command": "python3 scripts/render_reference_synthesis.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_reference_synthesis.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender a multi-source reference synthesis report for a skil", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_regression_history.py", + "command": "python3 scripts/render_regression_history.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_regression_history.py [-h] [--history-dir HISTORY_DIR]\n [--output OUTPUT]\n\nRender regression history from milestone snapshots.\n\noptions:\n -h, --help show this help message and exi", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_annotations.py", + "command": "python3 scripts/render_review_annotations.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_annotations.py [-h] [--annotations-json ANNOTATIONS_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD] [--write-template]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_studio.py", + "command": "python3 scripts/render_review_studio.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_studio.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender Review Studio 2.0 for a skill package.\n\npositional arguments:\n sk", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_viewer.py", + "command": "python3 scripts/render_review_viewer.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_viewer.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender a compact HTML review viewer for a skill package.\n\npositional argu", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_waivers.py", + "command": "python3 scripts/render_review_waivers.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_waivers.py [-h] [--waivers-json WAIVERS_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_interpretation.py", + "command": "python3 scripts/render_skill_interpretation.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_interpretation.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender the first-class HTML skill interpretation rep", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_os2_audit.py", + "command": "python3 scripts/render_skill_os2_audit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_os2_audit.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [skill_dir]\n\nRender a", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_os2_coverage.py", + "command": "python3 scripts/render_skill_os2_coverage.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_os2_coverage.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [skill_di", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_overview.py", + "command": "python3 scripts/render_skill_overview.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_overview.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender the HTML skill report for a skill package.\n\npositional argument", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_social_preview.py", + "command": "python3 scripts/render_social_preview.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_social_preview.py [-h] [--format {all,png,svg}]\n\nRender the repository social preview image assets.\n\noptions:\n -h, --help show this help message and exit\n --format {all,png,svg}\n Asset forma", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_system_model.py", + "command": "python3 scripts/render_system_model.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_system_model.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender a systems-thinking model for a skill package.\n\npositional arguments:\n sk", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_telemetry_hook_recipes.py", + "command": "python3 scripts/render_telemetry_hook_recipes.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_telemetry_hook_recipes.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--output-jsonl OUTPUT_JSONL]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_weekly_curator_report.py", + "command": "python3 scripts/render_weekly_curator_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_weekly_curator_report.py [-h] [--daily-json DAILY_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_claim_guard.py", + "command": "python3 scripts/render_world_class_claim_guard.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_claim_guard.py [-h] [--claim-surface CLAIM_SURFACE]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_evidence_intake.py", + "command": "python3 scripts/render_world_class_evidence_intake.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_evidence_intake.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_evidence_ledger.py", + "command": "python3 scripts/render_world_class_evidence_ledger.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_evidence_ledger.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--submissions-dir SUBMISSIONS_DIR]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_evidence_plan.py", + "command": "python3 scripts/render_world_class_evidence_plan.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_evidence_plan.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "command": "python3 scripts/render_world_class_operator_runbook.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_operator_runbook.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_preflight.py", + "command": "python3 scripts/render_world_class_preflight.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_preflight.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n [--output-md OU", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_submission_review.py", + "command": "python3 scripts/render_world_class_submission_review.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_submission_review.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/resource_boundary_check.py", + "command": "python3 scripts/resource_boundary_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: resource_boundary_check.py [-h]\n [--max-initial-tokens MAX_INITIAL_TOKENS]\n [--warn-skill-body-tokens WARN_SKILL_BODY_TOKENS]\n skill_d", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_conformance_suite.py", + "command": "python3 scripts/run_conformance_suite.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_conformance_suite.py [-h]\n [--target {openai,claude,agent-skills,vscode,generic}]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_description_optimization_suite.py", + "command": "python3 scripts/run_description_optimization_suite.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_description_optimization_suite.py [-h]\n [--history-snapshot-output HISTORY_SNAPSHOT_OUTPUT]\n [--snapshot-date SNAPSHOT_DATE]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_eval_suite.py", + "command": "python3 scripts/run_eval_suite.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_eval_suite.py [-h] [--eval-dir EVAL_DIR]\n [--description-file DESCRIPTION_FILE]\n [--baseline-description-file BASELINE_DESCRIPTION_FILE]\n [--output-file OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_output_eval.py", + "command": "python3 scripts/run_output_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_output_eval.py [-h] [--cases CASES] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--blind-pack-json BLIND_PACK_JSON]\n [--blind-pack-md BLIND_PACK", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_output_execution.py", + "command": "python3 scripts/run_output_execution.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_output_execution.py [-h] [--cases CASES]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--runner-command RUNNER_COMMAND]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/simulate_install.py", + "command": "python3 scripts/simulate_install.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: simulate_install.py [-h] [--package-dir PACKAGE_DIR]\n [--install-root INSTALL_ROOT]\n [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n [--generated-at G", + "stderr_excerpt": "" + }, + { + "path": "scripts/summarize_user_signals.py", + "command": "python3 scripts/summarize_user_signals.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: summarize_user_signals.py [-h] --source SOURCE\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--min-support MIN_SUPPORT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/sync_local_install.py", + "command": "python3 scripts/sync_local_install.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: sync_local_install.py [-h] [--root ROOT] [--install-dir INSTALL_DIR]\n [--package-dir PACKAGE_DIR]\n [--generated-at GENERATED_AT]\n [--skip-install-pre", + "stderr_excerpt": "" + }, + { + "path": "scripts/telemetry_native_host.py", + "command": "python3 scripts/telemetry_native_host.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: telemetry_native_host.py [-h] [--output-jsonl OUTPUT_JSONL]\n [--message-json MESSAGE_JSON] [--stdio]\n [--dry-run] [--write-manifest WRITE_MANIFEST]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/trigger_eval.py", + "command": "python3 scripts/trigger_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: trigger_eval.py [-h] [--description DESCRIPTION]\n [--description-file DESCRIPTION_FILE]\n [--baseline-description BASELINE_DESCRIPTION]\n [--baseline-description-file BA", + "stderr_excerpt": "" + }, + { + "path": "scripts/trust_check.py", + "command": "python3 scripts/trust_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: trust_check.py [-h] [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n [--skip-help-smoke]\n [--help-smoke-timeout HELP_SMOKE_TIMEOUT]\n [skill_dir]\n\nRun Skill OS trust a", + "stderr_excerpt": "" + }, + { + "path": "scripts/upgrade_check.py", + "command": "python3 scripts/upgrade_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: upgrade_check.py [-h] --previous-package-json PREVIOUS_PACKAGE_JSON\n [--current-package-json CURRENT_PACKAGE_JSON]\n [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/validate_skill.py", + "command": "python3 scripts/validate_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: validate_skill.py [-h] skill_dir\n\nValidate a skill package.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help show this help message and exit", + "stderr_excerpt": "" + }, + { + "path": "scripts/verify_package.py", + "command": "python3 scripts/verify_package.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: verify_package.py [-h] [--package-dir PACKAGE_DIR]\n [--expectations EXPECTATIONS]\n [--registry-json REGISTRY_JSON]\n [--output-json OUTPUT_JSON] [--output-md OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/yao.py", + "command": "python3 scripts/yao.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: yao.py [-h] [--record-cli-telemetry] [--no-cli-telemetry]\n [--telemetry-events-jsonl TELEMETRY_EVENTS_JSONL]\n {init,quickstart,validate,optimize-description,promote-check,python-compat,architecture-audit,rev", + "stderr_excerpt": "" + } + ], + "skipped": [ + { + "path": "scripts/adaptation_patch_safety.py", + "reason": "internal module" + }, + { + "path": "scripts/adaptation_report_contracts.py", + "reason": "internal module" + }, + { + "path": "scripts/benchmark_release_lock.py", + "reason": "internal module" + }, + { + "path": "scripts/build_skill_atlas_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/build_skill_atlas_opportunities.py", + "reason": "internal module" + }, + { + "path": "scripts/compile_skill_targets.py", + "reason": "internal module" + }, + { + "path": "scripts/cross_packager_contracts.py", + "reason": "internal module" + }, + { + "path": "scripts/description_optimizer_reporting.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_artifact_roles.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_core.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_phase_queue.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_release.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_skill_os2_review.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_world_class.py", + "reason": "internal module" + }, + { + "path": "scripts/html_rendering.py", + "reason": "internal module" + }, + { + "path": "scripts/output_review_privacy.py", + "reason": "internal module" + }, + { + "path": "scripts/reference_synthesis_markdown.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_action_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_actions.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_data.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_formatting.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_gate_contract.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_gate_helpers.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_gates.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_output_review.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_panels.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_skillops.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_waivers.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_world_class.py", + "reason": "internal module" + }, + { + "path": "scripts/review_viewer_data.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_ir_paths.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_os2_coverage_markdown.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_charts.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_i18n.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_metrics.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_model.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_sections.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_sources.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_world_class.py", + "reason": "internal module" + }, + { + "path": "scripts/skillops_opportunity.py", + "reason": "internal module" + }, + { + "path": "scripts/trust_check_scripts.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_evidence_contract.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_human_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_native_permission_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_native_telemetry_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_operator_runbook_coordination.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_phase_queue.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_preflight_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_preflight_markdown.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_provider_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_repair_checklist.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_source_checks.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_submission_kit_rendering.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_submission_matrix.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_adaptation_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_config.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_create_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_distribution_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_operator_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_output_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser_operations.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser_operator.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_report_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_runtime.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_telemetry.py", + "reason": "internal module" + } + ] + }, + "permission_governance": { + "present": true, + "path": "security/permission_policy.json", + "schema_version": "1.0", + "reviewed_at": "2026-06-13", + "required_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "approved_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "invalid_capabilities": [], + "expired_capabilities": [], + "approval_count": 3, + "required_count": 3, + "missing_count": 0, + "invalid_count": 0, + "expired_count": 0, + "approvals": [ + { + "capability": "network", + "status": "approved", + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "reviewer": "Yao Team", + "scope": "Only scripts declared in security/network_policy.json may perform outbound requests.", + "reason": "Network use is bounded to public GitHub metadata, benchmark discovery, update checks, and reviewed provider output-eval endpoints, with allowed hosts reviewed in security/network_policy.json.", + "expires_at": "2026-09-30", + "evidence": [ + "security/network_policy.json", + "reports/security_trust_report.md" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + }, + "validation": [] + }, + { + "capability": "file_write", + "status": "approved", + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "reviewer": "Yao Team", + "scope": "Local generated reports, registry metadata, temporary test outputs, and package artifacts under the skill workspace.", + "reason": "File writes are required to create auditable reports, compiled target contracts, package artifacts, and local test fixtures; package safety checks exclude generated temp outputs from archives.", + "expires_at": "2026-09-30", + "evidence": [ + "reports/package_verification.md", + "reports/install_simulation.md", + "security/permission_policy.md" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + }, + "validation": [] + }, + { + "capability": "subprocess", + "status": "approved", + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "reviewer": "Yao Team", + "scope": "Local deterministic validation, packaging, install simulation, and CI orchestration commands.", + "reason": "Subprocess use is limited to local verification and packaging flows that are surfaced through CLI help smoke tests and CI targets.", + "expires_at": "2026-09-30", + "evidence": [ + "reports/security_trust_report.md", + "reports/review-studio.html" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + }, + "validation": [] + } + ] + }, + "trust_metadata": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "artifacts": { + "json": "reports/security_trust_report.json", + "markdown": "reports/security_trust_report.md" + } + }, + "python_compatibility": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-13", + "root": ".", + "summary": { + "target_python": "3.11", + "file_count": 235, + "issue_count": 0, + "syntax_error_count": 0, + "fstring_311_violation_count": 0, + "decision": "pass" + }, + "rules": [ + { + "key": "current-python-syntax", + "reason": "Every scanned Python source file must compile under the running interpreter." + }, + { + "key": "fstring-expression-backslash", + "reason": "Python 3.11 rejects backslashes inside f-string expressions; keep escaping outside the expression." + } + ], + "files": [ + { + "path": "examples/complex-release-orchestrator/generated-skill/scripts/build_release_packet.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "examples/governed-incident-command/generated-skill/scripts/build_incident_packet.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "reports/blind-human-review-2026-06-29/generate_pack.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/adaptation_patch_safety.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/adaptation_report_contracts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/adjudicate_output_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/apply_adaptation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/benchmark_release_lock.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_confusion_matrix.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_skill_atlas.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_skill_atlas_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/build_skill_atlas_opportunities.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/check_update.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/ci_test.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/collect_feedback.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/compile_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/compile_skill_targets.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/context_sizer.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/create_iteration_snapshot.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/cross_packager.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/cross_packager_contracts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/description_optimizer_reporting.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/diff_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/emit_telemetry_event.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_artifact_roles.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_core.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_phase_queue.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_release.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_skill_os2_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/evidence_consistency_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/export_skill_ir.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/github_benchmark_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/governance_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/html_rendering.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/import_output_review_decisions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/import_telemetry_events.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/init_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/judge_blind_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/lint_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/local_output_eval_runner.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/optimize_description.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/output_review_privacy.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/prepare_output_review_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/prepare_world_class_submission_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/probe_runtime_permissions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/promotion_checker.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/propose_adaptation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/provider_output_eval_runner.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/python_compat_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/reference_synthesis_markdown.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/registry_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_adoption_drift_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_architecture_maintainability.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_artifact_design_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_baseline_compare.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_context_reports.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_daily_skillops_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_description_drift_history.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_eval_dashboard.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_evidence_consistency.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_intent_confidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_intent_dialogue.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_iteration_directions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_iteration_ledger.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_output_risk_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_portability_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_prompt_quality_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_reference_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_reference_synthesis.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_regression_history.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_annotations.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_studio.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_viewer.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_review_waivers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_interpretation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_os2_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_os2_coverage.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_skill_overview.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_social_preview.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_system_model.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_telemetry_hook_recipes.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_weekly_curator_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_claim_guard.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_evidence_intake.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_evidence_ledger.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_evidence_plan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_preflight.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/render_world_class_submission_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/resource_boundary_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_action_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_actions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_data.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_formatting.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_gate_contract.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_gate_helpers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_gates.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_output_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_panels.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_skillops.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_waivers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_studio_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/review_viewer_data.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_conformance_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_description_optimization_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_eval_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_output_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/run_output_execution.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/simulate_install.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_ir_paths.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_os2_coverage_markdown.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_charts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_i18n.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_metrics.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_model.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_sections.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_sources.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skill_report_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/skillops_opportunity.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/summarize_user_signals.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/sync_local_install.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/telemetry_native_host.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/trigger_eval.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/trust_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/trust_check_scripts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/upgrade_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/validate_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/verify_package.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_evidence_contract.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_human_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_native_permission_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_native_telemetry_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_operator_runbook_coordination.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_phase_queue.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_preflight_layout.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_preflight_markdown.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_provider_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_repair_checklist.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_source_checks.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_submission_kit_rendering.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/world_class_submission_matrix.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_adaptation_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_config.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_create_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_distribution_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_operator_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_output_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser_evidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser_operations.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_parser_operator.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_report_commands.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_runtime.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "scripts/yao_cli_telemetry.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/review_studio_helpers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/review_studio_world_class_assertions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_adaptation_safety.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_adapter_snapshots.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_adoption_drift.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_architecture_maintainability.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_artifact_design_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_baseline_compare.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_benchmark_reproducibility.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_compile_skill.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_conformance_suite.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_daily_skillops.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_description_optimization.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_evidence_consistency.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_failure_regressions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_feedback.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_github_benchmark_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_html_rendering.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_install_simulation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_intent_confidence.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_iteration_directions.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_local_install_sync.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_operator_ux.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_eval_lab.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_execution_runs.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_review_adjudication.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_review_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_output_risk_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_package_verification.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_packager_failures.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_portability_report.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_promotion_checker.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_prompt_quality_profile.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_python_compat_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_quality_checks.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_reference_scan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_reference_synthesis.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_registry_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_annotations.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_studio.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_viewer.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_review_waivers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_route_confusion.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_runtime_permission_probes.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_security_boundaries.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_atlas.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_interpretation.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_ir.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_ir_paths.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_os2_audit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_os2_coverage.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_overview.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_report_charts.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skill_report_metrics.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_skillops_opportunity.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_system_model.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_emit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_hooks.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_import.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_telemetry_native_host.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_trust_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_upgrade_check.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_weekly_curator.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_claim_guard.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_evidence_intake.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_evidence_ledger.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_evidence_plan.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_operator_runbook.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_preflight.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_submission_kit.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_world_class_submission_review.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_yao_cli.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/verify_yao_cli_world_class.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/world_class_human_fixtures.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/world_class_native_telemetry_fixtures.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/yao_cli_helpers.py", + "ok": true, + "issue_count": 0, + "issues": [] + }, + { + "path": "tests/yao_cli_report_refresh.py", + "ok": true, + "issue_count": 0, + "issues": [] + } + ], + "issues": [], + "artifacts": { + "json": "reports/python_compatibility.json", + "markdown": "reports/python_compatibility.md" + } + }, + "architecture_maintainability": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-13", + "skill_dir": ".", + "summary": { + "python_file_count": 232, + "script_file_count": 155, + "test_file_count": 77, + "internal_module_count": 71, + "cli_script_count": 86, + "command_handler_count": 71, + "entrypoint_command_handler_count": 18, + "command_module_count": 7, + "warn_line_threshold": 900, + "watch_line_threshold": 720, + "early_watch_line_threshold": 600, + "block_line_threshold": 1500, + "largest_file_lines": 719, + "watchlist_count": 0, + "early_watchlist_count": 6, + "hotspot_count": 0, + "blocker_count": 0, + "decision": "pass" + }, + "largest_files": [ + { + "path": "tests/verify_evidence_consistency.py", + "lines": 719, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_world_class_evidence_intake.py", + "lines": 703, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_yao_cli.py", + "lines": 700, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "lines": 685, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_evidence_consistency.py", + "lines": 676, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "lines": 651, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "tests/verify_output_review_adjudication.py", + "lines": 599, + "kind": "test", + "severity": "pass", + "early_watch": false, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/render_skill_overview.py", + "lines": 588, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "tests/verify_world_class_evidence_ledger.py", + "lines": 587, + "kind": "test", + "severity": "pass", + "early_watch": false, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/build_skill_atlas.py", + "lines": 586, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/optimize_description.py", + "lines": 585, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/trust_check.py", + "lines": 582, + "kind": "cli-script", + "severity": "pass", + "early_watch": false, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + } + ], + "watchlist": [], + "early_watchlist": [ + { + "path": "tests/verify_evidence_consistency.py", + "lines": 719, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_world_class_evidence_intake.py", + "lines": 703, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "tests/verify_yao_cli.py", + "lines": 700, + "kind": "test", + "severity": "pass", + "early_watch": true, + "recommendation": "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "lines": 685, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_evidence_consistency.py", + "lines": 676, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "lines": 651, + "kind": "cli-script", + "severity": "pass", + "early_watch": true, + "recommendation": "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + } + ], + "hotspots": [], + "actions": [], + "artifacts": { + "json": "reports/architecture_maintainability.json", + "markdown": "reports/architecture_maintainability.md" + } + }, + "context_budget": { + "ok": true, + "failures": [], + "warnings": [], + "stats": { + "context_budget_tier": "production", + "context_budget_limit": 1000, + "skill_body_tokens": 794, + "other_text_tokens": 1200569, + "estimated_initial_load_tokens": 987, + "estimated_total_text_tokens": 1201363, + "deferred_resource_tokens": 531767, + "deferred_resource_warn_threshold": 120000, + "deferred_resource_dirs": [ + { + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155 + }, + { + "path": "references", + "estimated_tokens": 28292, + "file_count": 35 + }, + { + "path": "evals", + "estimated_tokens": 22731, + "file_count": 27 + }, + { + "path": "assets", + "estimated_tokens": 11176, + "file_count": 4 + } + ], + "large_deferred_resource_dirs": [ + { + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155 + } + ], + "deferred_resource_governance": { + "status": "governed", + "large_dir_count": 1, + "governed_large_dir_count": 1, + "directories": [ + { + "status": "governed", + "evidence": [ + "reports/security_trust_report.json", + "reports/architecture_maintainability.json", + "reports/python_compatibility.json" + ], + "reasons": [ + "trust report covers scripts", + "architecture report has no script hotspots or blockers", + "Python compatibility report has no issues" + ], + "missing": [], + "path": "scripts", + "estimated_tokens": 469568, + "file_count": 155, + "rationale": "Script resources are deterministic deferred tools, not initial-load prompt context." + } + ], + "summary": "Large deferred resources are indexed and backed by evidence." + }, + "relevant_file_count": 591, + "unused_resource_dirs": [], + "quality_signal_points": 140, + "quality_density": 141.8 + } + }, + "promotion": { + "ok": true, + "summary": { + "promote": 0, + "keep_current": 3, + "blocked": 0 + }, + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "winner_tokens": 54, + "current_tokens": 54, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "blind_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "adversarial_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.644, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "yao-meta-skill", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Create and improve agent skills.", + "tokens": 8, + "dev_errors": 1, + "holdout_errors": 0 + }, + { + "name": "yao-meta-skill", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Minimal", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 41, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 56, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 60, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Trigger when requests mention workflows, prompts, transcripts, or docs and the job is to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 77, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 83, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.089, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.496, + "max_non_trigger_score": 0.211, + "score_gap": 0.285, + "threshold_margin": 0.016, + "positive_threshold_buffer": 0.016, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.805, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "team-frontend-review", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "tokens": 52, + "dev_errors": 3, + "holdout_errors": 0 + }, + { + "name": "team-frontend-review", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 62, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 64, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Trigger when requests mention React components, forms, frontend diffs, or UI code and the job is to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 84, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 90, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 1, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.45, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.037, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.057, + "score_gap": 0.023, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.423, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.82, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.645, + "max_non_trigger_score": 0.228, + "score_gap": 0.417, + "threshold_margin": 0.165, + "positive_threshold_buffer": 0.165, + "negative_threshold_buffer": 0.252, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.907, + "mean_non_trigger_score": 0.083, + "mean_near_neighbor_score": 0.139, + "min_positive_score": 0.815, + "max_non_trigger_score": 0.277, + "score_gap": 0.538, + "threshold_margin": 0.063, + "positive_threshold_buffer": 0.475, + "negative_threshold_buffer": 0.063, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_gap_present", + "current_holdout_risk", + "adversarial_watch_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.709, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "governed-incident-command", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, and stakeholder requirements. Use when asked to standardize incident review, severity assessment, incident communication, or incident packet assembly into one reusable operational skill. Do not use for generic debugging, one-off incident explanations, or brainstorming possible response ideas.", + "tokens": 93, + "dev_errors": 0, + "holdout_errors": 1 + }, + { + "name": "governed-incident-command", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Trigger when requests mention alerts, incident notes, transcripts, or stakeholder requirements and the job is to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 51, + "dev_errors": 1, + "holdout_errors": 1, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + } + ] + }, + "atlas": { + "ok": true, + "workspace_root": ".", + "summary": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + }, + "scope_policy": { + "present": true, + "path": "skill_atlas/policy.json", + "schema_version": "1.0", + "rules": [ + { + "path_prefix": "examples/", + "scope": "example", + "actionable": false, + "reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package." + }, + { + "path_prefix": "tests/fixtures/", + "scope": "test-fixture", + "actionable": false, + "reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers." + }, + { + "path_prefix": "geo-ranking-article-generator", + "scope": "embedded-generated-skill", + "actionable": false, + "reason": "This nested generated skill is retained as local evidence and should not gate the root Meta Skill release." + } + ] + }, + "catalog": { + "workspace_root": ".", + "generated_at": "2026-06-13", + "skills": [ + { + "name": "yao-meta-skill", + "path": ".", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "owner": "Yao Team", + "version": "1.1.0", + "status": "active", + "maturity": "governed", + "updated_at": "2026-03-31", + "review_cadence": "quarterly", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "resources": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py", + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md", + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "token_set": [ + "checks", + "docs", + "evals", + "evaluate", + "exclude", + "improve", + "install", + "migration", + "no-skill", + "package", + "prompts", + "release", + "routing", + "scripts", + "sops", + "summary", + "tests", + "translation", + "trigger", + "workflows", + "优化已有", + "评测" + ], + "atlas_scope": "release", + "actionable": true, + "scope_reason": "default release-actionable skill", + "telemetry": { + "report_present": true, + "report": "reports/adoption_drift_report.json", + "risk_band": "low", + "event_count": 1, + "adoption_sample_count": 1, + "adoption_rate": 100.0, + "candidate_count": 0 + } + }, + { + "name": "release-orchestrator", + "path": "examples/complex-release-orchestrator/generated-skill", + "description": "Coordinate software release preparation, rollout readiness, migration notes, rollback planning, stakeholder communication, and final go/no-go release packets. Use when asked to prepare a release packet, review release readiness, or turn scattered launch notes into one reusable release workflow. Do not use for generic project planning, one-off announcement drafting, or high-level release theory.", + "owner": "Yao Team", + "version": "1.0.0", + "status": "active", + "maturity": "production", + "updated_at": "2026-03-31", + "review_cadence": "quarterly", + "targets": [], + "resources": [ + "scripts/build_release_packet.py", + "references/migration-template.md", + "references/release-checklist.md", + "references/risk-matrix.md", + "references/stakeholder-comms.md" + ], + "token_set": [ + "announcement", + "asked", + "communication", + "coordinate", + "do", + "drafting", + "final", + "generic", + "go", + "high-level", + "launch", + "migration", + "no-go", + "not", + "notes", + "one", + "one-off", + "or", + "packet", + "packets", + "planning", + "preparation", + "prepare", + "project", + "readiness", + "release", + "review", + "rollback", + "rollout", + "scattered", + "software", + "stakeholder", + "theory", + "workflow" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/complex-release-orchestrator/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "a11y", + "accessibility", + "asked", + "code", + "common", + "components", + "forms", + "frontend", + "issues", + "missing", + "or", + "pre-merge", + "quality", + "react", + "regressions", + "review", + "risky", + "security", + "states", + "ui", + "ux" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/final/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "description": "Review frontend code before merge.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "before", + "code", + "frontend", + "merge", + "review" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/v0/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "description": "Review frontend code for accessibility, risky UI security issues, missing loading and error states, and pre-merge regressions. Use when asked to review React, UI, forms, frontend components, or merge-readiness for the frontend.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "accessibility", + "asked", + "code", + "components", + "error", + "forms", + "frontend", + "issues", + "loading", + "merge-readiness", + "missing", + "or", + "pre-merge", + "react", + "regressions", + "review", + "risky", + "security", + "states", + "ui" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/v1/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "owner": "Incident Operations Team", + "version": "1.0.0", + "status": "active", + "maturity": "governed", + "updated_at": "2026-03-31", + "review_cadence": "monthly", + "targets": [ + "openai", + "claude", + "generic" + ], + "resources": [ + "scripts/build_incident_packet.py", + "references/comms-policy.md", + "references/review-policy.md", + "references/severity-matrix.md" + ], + "token_set": [ + "asked", + "assemble", + "assessment", + "build", + "command", + "communication", + "governed", + "incident", + "or", + "packets", + "review", + "run", + "severity", + "standardize" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/governed-incident-command/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "description": "Clean messy meeting notes into structured markdown summaries. Use when asked to organize meeting notes, extract action items, separate decisions from open questions, or turn rough notes into a clean recap.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "action", + "asked", + "clean", + "decisions", + "extract", + "items", + "markdown", + "meeting", + "messy", + "notes", + "open", + "or", + "organize", + "questions", + "recap", + "rough", + "separate", + "structured", + "summaries" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/simple-note-cleanup/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [ + "references/checklist.md" + ], + "token_set": [ + "a11y", + "accessibility", + "asked", + "check", + "code", + "components", + "frontend", + "missing", + "or", + "pre-merge", + "react", + "regressions", + "rendering", + "review", + "run", + "security", + "states", + "ui", + "unsafe", + "ux" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/team-frontend-review/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "description": "Test fixture for governance validation.", + "owner": "", + "version": "1.0.0", + "status": "frozen", + "maturity": "governed", + "updated_at": "2026/03/31", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "fixture", + "governance", + "test", + "validation" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/governance_invalid_manifest/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "description": "Broken YAML fixture for packager failure testing.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "broken", + "failure", + "fixture", + "packager", + "testing", + "yaml" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/package_invalid_yaml/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "description": "Broken fixture for packager failure testing.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "broken", + "failure", + "fixture", + "packager", + "testing" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/package_missing_interface_field/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + } + ], + "summary": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + } + }, + "route_collisions": [ + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/final", + "path_b": "examples/evolution-frontend-review/v1", + "score": 0.708, + "status": "collision", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + }, + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/final", + "path_b": "examples/team-frontend-review/generated-skill", + "score": 0.64, + "status": "collision", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + }, + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/v1", + "path_b": "examples/team-frontend-review/generated-skill", + "score": 0.538, + "status": "collision", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + }, + { + "skill_a": "broken-yaml-skill", + "skill_b": "broken-skill", + "path_a": "tests/fixtures/package_invalid_yaml", + "path_b": "tests/fixtures/package_missing_interface_field", + "score": 0.833, + "status": "collision", + "actionable": false, + "scope_a": "test-fixture", + "scope_b": "test-fixture" + }, + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/final", + "path_b": "examples/evolution-frontend-review/v0", + "score": 1.0, + "status": "duplicate-name", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + } + ], + "actionable_route_collisions": [], + "dependency_graph": { + "nodes": [ + { + "name": "yao-meta-skill", + "path": "." + }, + { + "name": "release-orchestrator", + "path": "examples/complex-release-orchestrator/generated-skill" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1" + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field" + } + ], + "shared_resources": [] + }, + "stale_skills": [ + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill", + "reason": "review overdue by cadence monthly", + "age_days": 74, + "allowed_days": 31, + "actionable": false, + "scope": "example" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + } + ], + "actionable_stale_skills": [], + "owner_review_gaps": [ + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "missing": [ + "owner", + "review_cadence" + ], + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "test-fixture" + } + ], + "actionable_owner_review_gaps": [], + "drift_signals": [], + "actionable_drift_signals": [], + "no_route_opportunities": [], + "artifacts": { + "catalog": "skill_atlas/catalog.json", + "route_overlap_matrix": "skill_atlas/route_overlap_matrix.csv", + "dependency_graph": "skill_atlas/dependency_graph.json", + "stale_skills": "skill_atlas/stale_skills.json", + "owner_review_gaps": "skill_atlas/owner_review_gaps.json", + "drift_signals": "skill_atlas/drift_signals.json", + "no_route_opportunities": "skill_atlas/no_route_opportunities.json", + "report_json": "reports/skill_atlas.json", + "report_html": "reports/skill_atlas.html" + } + }, + "adoption_drift": { + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-07-02T06:33:44Z", + "skill_dir": ".", + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "summary": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 100.0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "failure_types": {}, + "source_types": { + "manual": 1 + }, + "command_counts": {} + }, + "adoption_by_skill": [ + { + "skill": "yao-meta-skill", + "events": 1, + "adoption_events": 1, + "accepted": 1, + "edited": 0, + "rejected": 0, + "missed": 0, + "adoption_rate": 100.0 + } + ], + "next_iteration_candidates": [], + "recent_events": [ + { + "command": "unknown", + "event": "skill_activation", + "skill": "yao-meta-skill", + "source": "manual", + "version": "1.1.0", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "timestamp": "2026-06-13T10:00:00Z" + } + ], + "failures": [], + "artifacts": { + "events_jsonl": "tests/tmp_review_studio/telemetry_events.jsonl", + "json": "reports/adoption_drift_report.json", + "markdown": "reports/adoption_drift_report.md" + } + }, + "daily_skillops": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02T06:33:07Z", + "skill_dir": ".", + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "operations_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "explicit_source_required_for_scan": true, + "implicit_private_log_scan": false, + "raw_content_stored": false, + "redacted_excerpts_only": true, + "proposal_only": true, + "approval_required_for_writes": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "daily_report_counts_as_world_class_evidence": false + }, + "report_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "source_supplied", + "pattern_count", + "proposal_count", + "approval_count", + "pending_review_count", + "applied_count", + "rollback_count", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_pending_count", + "release_lock_ready", + "evidence_consistency_ok", + "writes_source_files", + "auto_patch_enabled", + "failure_count" + ], + "source_of_truth": [ + "summary", + "operations_contract" + ] + }, + "source": { + "path": "evals/adaptation/user_signals.example.jsonl", + "fingerprint_sha256": "48f94a4f3b82f90db29921c13d09c765ec10aabfde1adce65710c137cc6c5abf", + "record_count": 8, + "explicit_source": true, + "raw_content_stored": false, + "redacted_excerpts_only": true + }, + "patterns": [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated report-language signals.", + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "support_count": 5, + "confidence": 0.95, + "reason": "5 redacted records matched repeated artifact-design signals.", + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated governance signals.", + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + "evidence": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated artifact-format signals.", + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + "evidence": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated quality-gate signals.", + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + "evidence": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "opportunity_summary": { + "opportunity_count": 5, + "top_score": 88, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "action_type_counts": { + "patch_existing_skill": 3, + "agents_update": 1, + "add_eval": 1 + }, + "decision_counts": { + "ready_for_approval_review": 1, + "proposal_review": 4 + } + }, + "opportunities": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "decision_policy": { + "schema_version": "1.0", + "contract": "skillops-opportunity-scoring", + "score_range": [ + 0, + 100 + ], + "score_bands": { + "85-100": "ready_for_approval_review", + "70-84": "proposal_review", + "50-69": "observe_more_evidence", + "0-49": "report_only_or_no_action" + }, + "action_types": [ + "add_eval", + "agents_update", + "patch_existing_skill", + "report_only" + ], + "writes_source_files": false, + "auto_patch_enabled": false, + "approval_required_for_writes": true + }, + "approval": { + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "release_state": { + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true + }, + "actions": [ + { + "key": "review-adaptation-proposals", + "priority": "high", + "action": "Review proposal-only adaptation items before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for the pending world-class ledger entries." + } + ], + "failures": [], + "source_reports": { + "patterns": "reports/user_patterns.json", + "proposals": "reports/adaptation_proposals.json", + "approval_ledger": "reports/adaptation_approval_ledger.json", + "regression": "reports/adaptation_regression_report.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "evidence_consistency": "reports/evidence_consistency.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json" + }, + "artifacts": { + "json": "reports/skillops/daily/2026-07-02.json", + "markdown": "reports/skillops/daily/2026-07-02.md" + }, + "source_path": "reports/skillops/daily/2026-07-02.json" + }, + "weekly_curator": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02T06:33:07Z", + "skill_dir": ".", + "decision": "curator-review", + "week_id": "2026-W27", + "daily_report_count": 5, + "opportunity_count": 25, + "unique_opportunity_count": 5, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "top_score": 88, + "skill_count": 11, + "actionable_portfolio_issue_count": 0, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "curator-review", + "week_id": "2026-W27", + "daily_report_count": 5, + "opportunity_count": 25, + "unique_opportunity_count": 5, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "top_score": 88, + "skill_count": 11, + "actionable_portfolio_issue_count": 0, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "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_contract": { + "schema_version": "1.0", + "contract": "weekly-skillops-curator-report", + "top_level_mirrors_summary": true, + "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" + ] + }, + "daily_reports": [ + "reports/skillops/daily/2026-06-16.json", + "reports/skillops/daily/2026-06-17.json", + "reports/skillops/daily/2026-06-20.json", + "reports/skillops/daily/2026-06-21.json", + "reports/skillops/daily/2026-07-02.json" + ], + "opportunity_summary": { + "action_type_counts": { + "add_eval": 1, + "agents_update": 1, + "patch_existing_skill": 3 + }, + "decision_counts": { + "proposal_review": 4, + "ready_for_approval_review": 1 + }, + "risk_level_counts": { + "low": 3, + "medium": 2 + } + }, + "curator_queue": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ], + "daily_report_date": "2026-06-16" + } + ], + "portfolio": { + "skill_count": 11, + "actionable_issue_count": 0, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "no_route_opportunity_count": 0 + }, + "release_state": { + "release_lock_ready": false, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4 + }, + "actions": [ + { + "key": "review-ready-opportunities", + "priority": "high", + "action": "Review the top ready-for-approval SkillOps opportunities before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for pending world-class ledger entries before public claims." + } + ], + "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": "reports/skillops/weekly/2026-W27.json", + "markdown": "reports/skillops/weekly/2026-W27.md" + }, + "source_path": "reports/skillops/weekly/2026-W27.json" + }, + "review_waivers": { + "schema_version": "1.0", + "ok": true, + "skill_dir": ".", + "generated_at": "2026-06-13", + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 2, + "waiverable_open_count": 1, + "non_waivable_count": 1 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "review_studio_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "review-waivers", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "world-class-evidence" + ], + "known_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "waiverable_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "non_waivable_gate_keys": [ + "review-waivers", + "world-class-evidence" + ] + }, + "waivers": [], + "waiver_candidates": [ + { + "gate_key": "output-lab", + "label": "Output Lab", + "status": "needs-reviewer-decision", + "waiver_allowed": true, + "decision_options": [ + "accepted-risk", + "false-positive", + "temporary-exception" + ], + "risk_summary": "review pending 5; model-executed 10; output failures 0", + "required_review": [ + "Reviewer confirms this release does not claim provider-backed or human-adjudicated output superiority.", + "Reviewer names the release scope and expiry date.", + "Reviewer links output_review_adjudication or output_execution evidence." + ], + "suggested_evidence": "reports/output_review_adjudication.md", + "suggested_command": "python3 scripts/yao.py review-waivers . --add-waiver --gate-key output-lab --reviewer \"\" --reason \"Output Lab has pending human/provider evidence; accepted only for this bounded review scope.\" --expires-at 2027-06-13 --evidence reports/output_review_adjudication.md", + "world_class_boundary": "Does not count as provider, human, or public world-class completion evidence." + }, + { + "gate_key": "world-class-evidence", + "label": "World-Class Evidence", + "status": "cannot-waive", + "waiver_allowed": false, + "risk_summary": "4 pending evidence entries; 1 human pending; 3 external pending", + "required_review": [ + "Do not use a waiver to claim public world-class readiness.", + "Either submit accepted ledger evidence or state that this release does not claim world-class completion.", + "Keep claim guard active until ledger summary.ready_to_claim_world_class is true." + ], + "suggested_evidence": "reports/world_class_evidence_ledger.md", + "suggested_command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard .", + "world_class_boundary": "Non-waivable completion boundary." + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "json": "reports/review_waivers.json", + "markdown": "reports/review_waivers.md" + } + }, + "review_annotations": { + "schema_version": "1.0", + "ok": true, + "skill_dir": ".", + "source": "tests/tmp_review_studio/empty_review_annotations_input.json", + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [], + "template_written": false, + "artifacts": { + "json": "reports/review_annotations.json", + "markdown": "reports/review_annotations.md" + } + }, + "adaptation_proposals": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02T06:33:07Z", + "skill_dir": ".", + "source_patterns": "reports/user_patterns.json", + "pattern_count": 5, + "proposal_count": 5, + "apply_supported": true, + "failure_count": 0, + "proposal_only": true, + "approval_required": true, + "writes_repository_files": false, + "allowlisted_targets_required": true, + "target_file_sha256_required_for_apply": true, + "approval_draft_supported": true, + "rollback_required_for_apply": true, + "apply_command_available": true, + "summary": { + "pattern_count": 5, + "proposal_count": 5, + "apply_supported": true, + "failure_count": 0 + }, + "proposal_contract": { + "proposal_only": true, + "approval_required": true, + "writes_repository_files": false, + "allowlisted_targets_required": true, + "target_file_sha256_required_for_apply": true, + "approval_draft_supported": true, + "rollback_required_for_apply": true, + "apply_command_available": true + }, + "report_contract": { + "schema_version": "1.0", + "contract": "adaptation-proposals", + "top_level_mirrors_summary": true, + "top_level_mirrors_proposal_contract": true, + "summary_fields": [ + "pattern_count", + "proposal_count", + "apply_supported", + "failure_count" + ], + "proposal_contract_fields": [ + "proposal_only", + "approval_required", + "writes_repository_files", + "allowlisted_targets_required", + "target_file_sha256_required_for_apply", + "approval_draft_supported", + "rollback_required_for_apply", + "apply_command_available" + ], + "source_of_truth": [ + "summary", + "proposal_contract" + ] + }, + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "change_type": "report-default-language", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "low", + "reason": "2 redacted records matched repeated report-language signals.", + "support_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "suggested_changes": [ + "Keep user-facing report copy Simplified Chinese by default.", + "Expose English through the existing language switch instead of mixing languages in the default view." + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ], + "rollback_plan": "Revert report language template changes and rerun the overview verifier.", + "evidence_refs": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "change_type": "artifact-ui-polish", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "medium", + "reason": "5 redacted records matched repeated artifact-design signals.", + "support_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "suggested_changes": [ + "Prefer vertical narrative sections with limited two-column layouts only when content has enough width.", + "Keep charts inline SVG, with captions and stable responsive constraints." + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ], + "rollback_plan": "Restore the previous report renderer and regenerate the demo report.", + "evidence_refs": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "change_type": "privacy-governance", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "low", + "reason": "2 redacted records matched repeated governance signals.", + "support_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "suggested_changes": [ + "Require explicit source paths for memory scans.", + "Generate proposals before any source patching.", + "Reserve automatic apply for a future approval ledger and rollback implementation." + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ], + "rollback_plan": "Remove the adaptive proposal artifacts and keep feedback/adoption drift as the only iteration inputs.", + "evidence_refs": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "change_type": "artifact-discoverability", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "low", + "reason": "2 redacted records matched repeated artifact-format signals.", + "support_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "suggested_changes": [ + "Include stable report paths in command output.", + "Document which artifacts are meant for human review." + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ], + "rollback_plan": "Revert CLI copy/documentation changes and keep artifact paths unchanged.", + "evidence_refs": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "change_type": "quality-gate", + "status": "proposal-only", + "requires_approval": true, + "write_allowed_without_approval": false, + "risk_level": "medium", + "reason": "2 redacted records matched repeated quality-gate signals.", + "support_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "suggested_changes": [ + "Add focused verifier coverage for every new adaptive behavior.", + "Refresh Skill OS 2.0 coverage so planned, partial, and covered states remain visible." + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ], + "rollback_plan": "Revert the new verifier and coverage status updates, then regenerate coverage reports.", + "evidence_refs": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "failures": [], + "artifacts": { + "json": "reports/adaptation_proposals.json", + "markdown": "reports/adaptation_proposals.md" + } + }, + "adaptation_approval_ledger": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-15T00:00:00Z", + "summary": { + "approval_count": 0, + "active_approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "approval_contract": { + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true + }, + "entries": [], + "approval_count": 0, + "active_approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true, + "report_contract": { + "schema_version": "1.0", + "contract": "adaptation-approval-ledger", + "top_level_mirrors_summary": true, + "top_level_mirrors_approval_contract": true, + "summary_fields": [ + "approval_count", + "active_approval_count", + "pending_review_count", + "applied_count", + "rollback_count" + ], + "approval_contract_fields": [ + "approval_required", + "patch_sha256_required", + "allowlisted_targets_required", + "target_file_sha256_required", + "approval_draft_supported", + "dry_run_default", + "writes_repository_files_only_with_apply", + "rollback_required" + ], + "source_of_truth": [ + "summary", + "approval_contract" + ] + } + }, + "adaptation_regression": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-15T00:00:00Z", + "skill_dir": ".", + "summary": { + "apply_supported": true, + "attempt_count": 0, + "approval_draft_count": 0, + "applied_count": 0, + "dry_run_count": 0, + "rollback_count": 0, + "regression_run_count": 0, + "regression_pass_count": 0, + "failure_count": 0 + }, + "apply_contract": { + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true, + "safe_regression_commands_only": true, + "rollback_on_failure_default": true + }, + "attempts": [], + "failures": [], + "artifacts": { + "json": "reports/adaptation_regression_report.json", + "approval_ledger": "reports/adaptation_approval_ledger.json" + }, + "apply_supported": true, + "attempt_count": 0, + "approval_draft_count": 0, + "applied_count": 0, + "dry_run_count": 0, + "rollback_count": 0, + "regression_run_count": 0, + "regression_pass_count": 0, + "failure_count": 0, + "approval_required": true, + "patch_sha256_required": true, + "allowlisted_targets_required": true, + "target_file_sha256_required": true, + "approval_draft_supported": true, + "dry_run_default": true, + "writes_repository_files_only_with_apply": true, + "rollback_required": true, + "safe_regression_commands_only": true, + "rollback_on_failure_default": true, + "report_contract": { + "schema_version": "1.0", + "contract": "adaptation-regression-report", + "top_level_mirrors_summary": true, + "top_level_mirrors_apply_contract": true, + "summary_fields": [ + "apply_supported", + "attempt_count", + "approval_draft_count", + "applied_count", + "dry_run_count", + "rollback_count", + "regression_run_count", + "regression_pass_count", + "failure_count" + ], + "apply_contract_fields": [ + "approval_required", + "patch_sha256_required", + "allowlisted_targets_required", + "target_file_sha256_required", + "approval_draft_supported", + "dry_run_default", + "writes_repository_files_only_with_apply", + "rollback_required", + "safe_regression_commands_only", + "rollback_on_failure_default" + ], + "source_of_truth": [ + "summary", + "apply_contract" + ] + } + }, + "world_class_evidence_ledger": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "decision": "evidence-pending", + "ready_to_claim_world_class": false, + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "summary": { + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "report_contract": { + "schema_version": "1.0", + "contract": "world-class-evidence-ledger", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "ready_to_claim_world_class", + "ledger_entry_count", + "source_accepted_count", + "accepted_count", + "pending_count", + "human_pending_count", + "external_pending_count", + "submitted_entry_count", + "reviewer_approved_submission_count", + "missing_submission_count", + "invalid_submission_count", + "source_check_count", + "source_pass_count", + "source_blocked_count", + "submitted_but_pending_count", + "source_accepted_without_valid_submission_count", + "overclaim_guard_active" + ], + "source_of_truth": "summary" + }, + "entries": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "pending", + "source_status": "pass", + "source_accepted": true, + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "pending", + "source_status": "human_required", + "source_accepted": false, + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_plan": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "task_count": 4, + "evidence_requirement_count": 4 + }, + "submissions": { + "directory": "evidence/world_class/submissions", + "ledger_counts_submission_as_completion": false, + "source_pass_requires_valid_submission": true + }, + "artifacts": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md", + "intake": "reports/world_class_evidence_intake.md" + } + }, + "world_class_evidence_intake": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "schema_present": true, + "ledger_entry_count": 4, + "template_count": 4, + "template_pass_count": 4, + "submission_count": 0, + "valid_submission_count": 0, + "invalid_submission_count": 0, + "operator_checklist_count": 4, + "operator_checklist_ready_count": 0, + "valid_packet_source_incomplete_count": 0, + "ready_for_external_collection": true, + "ready_for_ledger_review": false, + "ready_to_claim_world_class": false, + "overclaim_guard_active": true, + "decision": "awaiting-submissions" + }, + "schema": "evidence/world_class/intake.schema.json", + "templates": [ + { + "path": "evidence/world_class/templates/provider-holdout.intake.json", + "evidence_key": "provider-holdout", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 1, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + }, + { + "path": "evidence/world_class/templates/human-adjudication.intake.json", + "evidence_key": "human-adjudication", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 2, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + }, + { + "path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "evidence_key": "native-permission-enforcement", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 3, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + }, + { + "path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "evidence_key": "native-client-telemetry", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 2, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + } + ], + "submissions": [], + "operator_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": true, + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": false, + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": false, + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": false, + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_ledger": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md", + "pending_count": 4 + }, + "artifacts": { + "json": "reports/world_class_evidence_intake.json", + "markdown": "reports/world_class_evidence_intake.md" + } + }, + "world_class_evidence_preflight": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "evidence_item_count": 4, + "precheck_count": 14, + "precheck_pass_count": 9, + "precheck_missing_count": 1, + "precheck_optional_count": 1, + "precheck_human_required_count": 1, + "precheck_external_required_count": 2, + "collection_ready_count": 1, + "collection_blocked_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "repair_checklist_count": 11, + "repair_blocked_count": 11, + "repair_ready_count": 0, + "repair_phase_counts": { + "unblock-access": 4, + "collect-source": 7 + }, + "next_repair_action_id": "human-adjudication-precheck-human-reviewer", + "next_repair_phase": "unblock-access", + "next_repair_owner": "human reviewer", + "next_repair_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "repair_counts_as_completion": false, + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false, + "pending_count": 4, + "ready_to_claim_world_class": false, + "credential_value_exposed": false, + "preflight_counts_as_evidence": false, + "decision": "collection-preflight-blocked" + }, + "status_counts": { + "blocked": 3, + "ready-for-human-review": 1 + }, + "precheck_status_counts": { + "pass": 9, + "missing": 1, + "optional": 1, + "human-required": 1, + "external-required": 2 + }, + "items": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "blocked", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": true, + "prechecks": [ + { + "evidence_key": "provider-holdout", + "key": "output-cases", + "label": "Output eval cases", + "kind": "file", + "required": true, + "next_action": "Keep output holdout cases available before provider execution.", + "secret_value_redacted": false, + "path": "evals/output/cases.jsonl", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-runner", + "label": "Provider runner", + "kind": "file", + "required": true, + "next_action": "Use the provider runner instead of the local command runner for model-backed evidence.", + "secret_value_redacted": false, + "path": "scripts/provider_output_eval_runner.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-api-key", + "label": "Provider credential", + "kind": "env_any", + "required": true, + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "secret_value_redacted": true, + "env_any": [ + "OPENAI_API_KEY", + "DEEPSEEK_API_KEY" + ], + "status": "missing", + "actual": "not-set", + "set_count": 0, + "set_envs": [] + }, + { + "evidence_key": "provider-holdout", + "key": "provider-model", + "label": "Provider model", + "kind": "env", + "required": false, + "next_action": "Optionally set YAO_OUTPUT_EVAL_MODEL, or pass --provider-model for the selected provider.", + "secret_value_redacted": false, + "env": "YAO_OUTPUT_EVAL_MODEL", + "status": "optional", + "actual": "not-set", + "default": "provider-specific model, or pass --provider-model" + } + ], + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/provider-holdout.json", + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 1, + "submission_ref_ready_count": 1, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + } + ] + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "ready-for-human-review", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "prechecks": [ + { + "evidence_key": "human-adjudication", + "key": "review-kit", + "label": "Blind review kit", + "kind": "file", + "required": true, + "next_action": "Open the blind review kit and record real reviewer choices with required rationale.", + "secret_value_redacted": false, + "path": "reports/output_review_kit.html", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-template", + "label": "Decision template", + "kind": "file", + "required": true, + "next_action": "Import real A/B decisions with reviewer, reviewed_at, winner_variant, confidence, reason, and truthful blind-review attestation via `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication`.", + "secret_value_redacted": false, + "path": "reports/output_review_decisions.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-importer", + "label": "Decision importer", + "kind": "file", + "required": true, + "next_action": "Use the importer to reject raw content fields and normalize reviewer decisions before adjudication.", + "secret_value_redacted": false, + "path": "scripts/import_output_review_decisions.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "human-reviewer", + "label": "Human reviewer", + "kind": "human", + "required": true, + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "secret_value_redacted": false, + "status": "human-required", + "actual": "external-human-action" + } + ], + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/human-adjudication.json", + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 8, + "supporting_evidence_ready_count": 8, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ] + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "blocked", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "prechecks": [ + { + "evidence_key": "native-permission-enforcement", + "key": "permission-policy", + "label": "Permission policy", + "kind": "file", + "required": true, + "next_action": "Keep approved high-permission capabilities explicit.", + "secret_value_redacted": false, + "path": "security/permission_policy.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "permission-probes", + "label": "Runtime probes", + "kind": "file", + "required": true, + "next_action": "Refresh runtime permission probes after packaging changes.", + "secret_value_redacted": false, + "path": "reports/runtime_permission_probes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "native-guard", + "label": "Native guard", + "kind": "external", + "required": true, + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + } + ], + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 11, + "supporting_evidence_ready_count": 11, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ] + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "prechecks": [ + { + "evidence_key": "native-client-telemetry", + "key": "native-host", + "label": "Native telemetry host", + "kind": "file", + "required": true, + "next_action": "Use the native host to receive metadata-only client events.", + "secret_value_redacted": false, + "path": "scripts/telemetry_native_host.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "hook-recipes", + "label": "Hook recipes", + "kind": "file", + "required": true, + "next_action": "Refresh telemetry hook recipes before external client installation.", + "secret_value_redacted": false, + "path": "reports/telemetry_hook_recipes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "external-client", + "label": "External client", + "kind": "external", + "required": true, + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + } + ], + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/native-client-telemetry.json", + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ] + } + ], + "prechecks": [ + { + "evidence_key": "provider-holdout", + "key": "output-cases", + "label": "Output eval cases", + "kind": "file", + "required": true, + "next_action": "Keep output holdout cases available before provider execution.", + "secret_value_redacted": false, + "path": "evals/output/cases.jsonl", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-runner", + "label": "Provider runner", + "kind": "file", + "required": true, + "next_action": "Use the provider runner instead of the local command runner for model-backed evidence.", + "secret_value_redacted": false, + "path": "scripts/provider_output_eval_runner.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-api-key", + "label": "Provider credential", + "kind": "env_any", + "required": true, + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "secret_value_redacted": true, + "env_any": [ + "OPENAI_API_KEY", + "DEEPSEEK_API_KEY" + ], + "status": "missing", + "actual": "not-set", + "set_count": 0, + "set_envs": [] + }, + { + "evidence_key": "provider-holdout", + "key": "provider-model", + "label": "Provider model", + "kind": "env", + "required": false, + "next_action": "Optionally set YAO_OUTPUT_EVAL_MODEL, or pass --provider-model for the selected provider.", + "secret_value_redacted": false, + "env": "YAO_OUTPUT_EVAL_MODEL", + "status": "optional", + "actual": "not-set", + "default": "provider-specific model, or pass --provider-model" + }, + { + "evidence_key": "human-adjudication", + "key": "review-kit", + "label": "Blind review kit", + "kind": "file", + "required": true, + "next_action": "Open the blind review kit and record real reviewer choices with required rationale.", + "secret_value_redacted": false, + "path": "reports/output_review_kit.html", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-template", + "label": "Decision template", + "kind": "file", + "required": true, + "next_action": "Import real A/B decisions with reviewer, reviewed_at, winner_variant, confidence, reason, and truthful blind-review attestation via `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication`.", + "secret_value_redacted": false, + "path": "reports/output_review_decisions.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-importer", + "label": "Decision importer", + "kind": "file", + "required": true, + "next_action": "Use the importer to reject raw content fields and normalize reviewer decisions before adjudication.", + "secret_value_redacted": false, + "path": "scripts/import_output_review_decisions.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "human-reviewer", + "label": "Human reviewer", + "kind": "human", + "required": true, + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "secret_value_redacted": false, + "status": "human-required", + "actual": "external-human-action" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "permission-policy", + "label": "Permission policy", + "kind": "file", + "required": true, + "next_action": "Keep approved high-permission capabilities explicit.", + "secret_value_redacted": false, + "path": "security/permission_policy.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "permission-probes", + "label": "Runtime probes", + "kind": "file", + "required": true, + "next_action": "Refresh runtime permission probes after packaging changes.", + "secret_value_redacted": false, + "path": "reports/runtime_permission_probes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "native-guard", + "label": "Native guard", + "kind": "external", + "required": true, + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + }, + { + "evidence_key": "native-client-telemetry", + "key": "native-host", + "label": "Native telemetry host", + "kind": "file", + "required": true, + "next_action": "Use the native host to receive metadata-only client events.", + "secret_value_redacted": false, + "path": "scripts/telemetry_native_host.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "hook-recipes", + "label": "Hook recipes", + "kind": "file", + "required": true, + "next_action": "Refresh telemetry hook recipes before external client installation.", + "secret_value_redacted": false, + "path": "reports/telemetry_hook_recipes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "external-client", + "label": "External client", + "kind": "external", + "required": true, + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + } + ], + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + }, + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + }, + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "submissions": { + "directory": "evidence/world_class/submissions", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit_command": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions", + "submission_kit_prefill_command": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "preflight_counts_submission_as_completion": false, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 7, + "submission_ref_ready_count": 7, + "supporting_evidence_total_count": 31, + "supporting_evidence_ready_count": 31, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + } + }, + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json", + "submission_review": "reports/world_class_submission_review.json", + "operator_runbook": "reports/world_class_operator_runbook.json" + }, + "artifacts": { + "json": "reports/world_class_evidence_preflight.json", + "markdown": "reports/world_class_evidence_preflight.md", + "html": "reports/world_class_evidence_preflight.html" + } + }, + "world_class_submission_review": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "decision": "awaiting-submissions", + "ready_to_claim_world_class": false, + "review_item_count": 4, + "accepted_count": 0, + "awaiting_submission_count": 4, + "valid_packet_source_incomplete_count": 0, + "ready_for_ledger_review_count": 0, + "fix_submission_count": 0, + "unmatched_submission_count": 0, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "review_counts_submission_as_completion": false, + "summary": { + "review_item_count": 4, + "accepted_count": 0, + "awaiting_submission_count": 4, + "valid_packet_source_incomplete_count": 0, + "ready_for_ledger_review_count": 0, + "fix_submission_count": 0, + "unmatched_submission_count": 0, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "review_counts_submission_as_completion": false, + "decision": "awaiting-submissions" + }, + "report_contract": { + "schema_version": "1.0", + "contract": "world-class-submission-review", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "ready_to_claim_world_class", + "review_item_count", + "accepted_count", + "awaiting_submission_count", + "valid_packet_source_incomplete_count", + "ready_for_ledger_review_count", + "fix_submission_count", + "unmatched_submission_count", + "invalid_submission_count", + "source_check_count", + "source_pass_count", + "source_blocked_count", + "review_counts_submission_as_completion" + ], + "source_of_truth": "summary" + }, + "submissions": { + "directory": "evidence/world_class/submissions", + "review_counts_submission_as_completion": false + }, + "items": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": true, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": false, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": false, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": false, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "unmatched_submissions": [], + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json" + }, + "artifacts": { + "json": "reports/world_class_submission_review.json", + "markdown": "reports/world_class_submission_review.md" + } + }, + "world_class_operator_runbook": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "evidence_item_count": 4, + "pending_count": 4, + "accepted_count": 0, + "awaiting_submission_count": 4, + "ready_for_ledger_review_count": 0, + "valid_packet_source_incomplete_count": 0, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "repair_checklist_count": 11, + "repair_blocked_count": 11, + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false, + "coordination_step_count": 6, + "coordination_user_required_step_count": 6, + "coordination_pending_evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "coordination_counts_as_completion": false, + "release_gate_ready": false, + "release_gate_blocked_count": 4, + "release_gate_check_count": 5, + "release_gate_counts_as_completion": false, + "ready_to_claim_world_class": false, + "runbook_counts_as_completion": false, + "decision": "collect-evidence" + }, + "submissions": { + "directory": "evidence/world_class/submissions", + "runbook_counts_submission_as_completion": false + }, + "items": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": true, + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "current": "model-executed 10; token-observed 10", + "execution_runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ] + }, + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "blocked_source_check_count": 0, + "next_source_actions": [], + "repair_checklist": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ], + "repair_blocked_count": 1, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 1, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "current": "0/5 decisions; pending 5", + "execution_runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ] + }, + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "blocked_source_check_count": 5, + "next_source_actions": [ + "Record a reviewer choice and reason for every pair.", + "Every pair needs one valid human judgment.", + "Record reviewer and reviewed_at before adjudication can count.", + "Set reviewer_attestation only after choices are completed before opening the answer key.", + "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + ], + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "repair_blocked_count": 6, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "current": "native-enforced targets 0; installer-enforced targets 4", + "execution_runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ] + }, + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "blocked_source_check_count": 1, + "next_source_actions": [ + "Collect real target-client or external runtime guard proof." + ], + "repair_checklist": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "repair_blocked_count": 2, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "current": "external source events 0; adoption samples 1", + "execution_runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ] + }, + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "blocked_source_check_count": 1, + "next_source_actions": [ + "Import at least one metadata-only event from a real client." + ], + "repair_checklist": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "repair_blocked_count": 2, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + } + ], + "coordination_plan": [ + { + "step_id": "prepare-evidence-session", + "phase": "prepare", + "evidence_key": "", + "owner": "assistant + user", + "requires_user_input": true, + "user_action": "Confirm provider access, reviewer availability, target client path, and telemetry client path before collection starts.", + "assistant_action": "Run preflight and prepare submission drafts without accepting them as evidence.", + "external_dependency": "User-selected provider, reviewer, and real client surfaces.", + "command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "pass_condition": "Preflight lists the same pending evidence keys and no credential values are printed.", + "artifact": "reports/world_class_evidence_preflight.json", + "privacy_boundary": "Drafts and preflight are planning artifacts only; they do not count as evidence.", + "counts_as_completion": false + }, + { + "step_id": "collect-provider-holdout", + "phase": "provider-holdout", + "evidence_key": "provider-holdout", + "owner": "assistant + operator with provider credentials", + "requires_user_input": true, + "user_action": "Provide the selected provider API key through an environment variable and confirm the provider, model, endpoint, and API format to use.", + "assistant_action": "Run provider-backed output execution, verify aggregate timing and token metadata, then prepare the evidence packet.", + "external_dependency": "Valid provider credentials and a live provider endpoint.", + "command": "python3 scripts/yao.py output-exec . --provider-runner --provider-model --timeout-seconds 60", + "pass_condition": "reports/output_execution_runs.json has model_executed_count > 0 and token_observed_count > 0.", + "artifact": "reports/output_execution_runs.json", + "privacy_boundary": "Commit aggregate metadata only; do not commit API keys, raw prompts, raw outputs, or provider request payloads.", + "counts_as_completion": false + }, + { + "step_id": "collect-human-adjudication", + "phase": "human-review", + "evidence_key": "human-adjudication", + "owner": "human reviewer + assistant", + "requires_user_input": true, + "user_action": "Open the blind review kit, choose winners for all pairs, add reviewer metadata and reasons, and keep the answer key hidden until decisions are saved.", + "assistant_action": "Generate the review kit, import decisions, validate integrity, and prepare the human evidence packet.", + "external_dependency": "A real human reviewer who can make blind A/B judgments.", + "command": "python3 scripts/yao.py output-review-kit . && python3 scripts/yao.py output-review .", + "pass_condition": "reports/output_review_adjudication.json has pending_count == 0 and ready_for_human_evidence == true.", + "artifact": "reports/output_review_adjudication.json", + "privacy_boundary": "Store reviewer choices, reasons, hashes, and attestations; do not store raw prompts or answer-key exposure before decisions.", + "counts_as_completion": false + }, + { + "step_id": "collect-native-permission-enforcement", + "phase": "native-permission", + "evidence_key": "native-permission-enforcement", + "owner": "target client or installer integrator + assistant", + "requires_user_input": true, + "user_action": "Select a real target client or external installer guard that can enforce declared capabilities instead of metadata-only fallback.", + "assistant_action": "Run runtime permission probes, package verification, install simulation, and prepare the native enforcement evidence packet.", + "external_dependency": "A real target runtime, extension, client, or installer guard that can block undeclared capabilities.", + "command": "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "pass_condition": "reports/runtime_permission_probes.json has native_enforcement_count > 0 and failure_count == 0.", + "artifact": "reports/runtime_permission_probes.json", + "privacy_boundary": "Do not mark metadata fallback as native enforcement; keep residual risks visible for fallback targets.", + "counts_as_completion": false + }, + { + "step_id": "collect-native-client-telemetry", + "phase": "native-telemetry", + "evidence_key": "native-client-telemetry", + "owner": "real client integrator + assistant", + "requires_user_input": true, + "user_action": "Install the native host manifest in a real Browser, Chrome, IDE, or provider client and trigger a metadata-only event.", + "assistant_action": "Generate native host assets, import the external event JSONL, refresh adoption drift, and prepare the telemetry evidence packet.", + "external_dependency": "A real client process that emits metadata-only telemetry through the native host.", + "command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl --source external", + "pass_condition": "reports/adoption_drift_report.json has source_types.external > 0 and adoption_sample_count > 0.", + "artifact": "reports/adoption_drift_report.json", + "privacy_boundary": "Telemetry must remain metadata-only; do not store raw prompts, outputs, transcripts, notes, or messages.", + "counts_as_completion": false + }, + { + "step_id": "review-and-release-gate", + "phase": "release-gate", + "evidence_key": "", + "owner": "assistant + ledger reviewer", + "requires_user_input": true, + "user_action": "Approve only validated evidence packets and confirm the release wording after the claim guard passes.", + "assistant_action": "Run intake, submission review, ledger, claim guard, benchmark, evidence consistency, Review Studio, and CI before final publish.", + "external_dependency": "Accepted evidence packets for every pending world-class key.", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard . && make ci-test", + "pass_condition": "Ledger ready_to_claim_world_class, benchmark public_claim_ready, claim guard violation_count == 0, Review Studio has no blockers, and CI passes.", + "artifact": "reports/world_class_evidence_ledger.json", + "privacy_boundary": "Release evidence may include aggregate reports and hashes only; raw content and credentials remain out of git.", + "counts_as_completion": false + } + ], + "release_gate": { + "ready": false, + "decision": "blocked-until-evidence-accepted", + "check_count": 5, + "blocked_count": 4, + "checks": [ + { + "key": "world_class_ledger_ready", + "label": "World-class ledger ready", + "passed": false, + "current": "evidence-pending", + "expected": "ready_to_claim_world_class == true", + "artifact": "reports/world_class_evidence_ledger.json" + }, + { + "key": "claim_guard_clean", + "label": "Claim guard clean", + "passed": false, + "current": "violations 0; ledger ready False", + "expected": "violation_count == 0 and ledger_ready_to_claim_world_class == true", + "artifact": "reports/world_class_claim_guard.json" + }, + { + "key": "benchmark_public_claim_ready", + "label": "Benchmark public claim ready", + "passed": false, + "current": "public_claim_ready False", + "expected": "public_claim_ready == true", + "artifact": "reports/benchmark_reproducibility.json" + }, + { + "key": "review_studio_clean", + "label": "Review Studio clean", + "passed": false, + "current": "blockers 0; warnings 3", + "expected": "blocker_count == 0 and warning_count == 0", + "artifact": "reports/review-studio.json" + }, + { + "key": "evidence_consistency_clean", + "label": "Evidence consistency clean", + "passed": true, + "current": "consistent", + "expected": "decision == consistent and fail_count == 0", + "artifact": "reports/evidence_consistency.json" + } + ], + "blocked_checks": [ + { + "key": "world_class_ledger_ready", + "label": "World-class ledger ready", + "passed": false, + "current": "evidence-pending", + "expected": "ready_to_claim_world_class == true", + "artifact": "reports/world_class_evidence_ledger.json" + }, + { + "key": "claim_guard_clean", + "label": "Claim guard clean", + "passed": false, + "current": "violations 0; ledger ready False", + "expected": "violation_count == 0 and ledger_ready_to_claim_world_class == true", + "artifact": "reports/world_class_claim_guard.json" + }, + { + "key": "benchmark_public_claim_ready", + "label": "Benchmark public claim ready", + "passed": false, + "current": "public_claim_ready False", + "expected": "public_claim_ready == true", + "artifact": "reports/benchmark_reproducibility.json" + }, + { + "key": "review_studio_clean", + "label": "Review Studio clean", + "passed": false, + "current": "blockers 0; warnings 3", + "expected": "blocker_count == 0 and warning_count == 0", + "artifact": "reports/review-studio.json" + } + ], + "final_manual_check": "Run make ci-test in a clean worktree and verify GitHub Actions before converting the PR out of Draft.", + "counts_as_completion": false + }, + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json", + "preflight": "reports/world_class_evidence_preflight.json", + "submission_review": "reports/world_class_submission_review.json", + "claim_guard": "reports/world_class_claim_guard.json" + }, + "artifacts": { + "json": "reports/world_class_operator_runbook.json", + "markdown": "reports/world_class_operator_runbook.md", + "html": "reports/world_class_operator_runbook.html" + } + }, + "world_class_claim_guard": { + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "ledger_ready_to_claim_world_class": false, + "ledger_pending_count": 4, + "claim_surface_count": 201, + "json_claim_surface_count": 97, + "metadata_claim_surface_count": 98, + "package_claim_surface_count": 17, + "violation_count": 0, + "overclaim_guard_active": true, + "decision": "claim-guard-pass-evidence-pending" + }, + "rules": [ + { + "key": "ready-to-claim-true", + "reason": "ready_to_claim_world_class cannot be true while ledger evidence is pending.", + "pattern": "ready[\\s_-]*to[\\s_-]*claim[\\s_-]*world[\\s_-]*class\\s*[:=]\\s*`?true`?" + }, + { + "key": "world-class-ready-true", + "reason": "world-class readiness cannot be claimed before accepted external and human evidence exists.", + "pattern": "(public\\s+)?world[\\s_-]*class[\\s_-]*ready\\s*[:=]\\s*`?true`?" + }, + { + "key": "json-world-class-ready-true", + "reason": "machine-readable claim fields must remain false until the ledger is ready.", + "pattern": "\"(?:ready_to_claim_world_class|world_class_ready|public_world_class_ready)\"\\s*:\\s*true" + }, + { + "key": "completion-phrase", + "reason": "completion language is blocked until the world-class ledger is accepted.", + "pattern": "(public\\s+)?world[\\s_-]*class\\s+(?:complete|completed|achieved|certified)" + }, + { + "key": "zh-completion-phrase", + "reason": "中文完成态表述必须等到 ledger ready 后才能出现。", + "pattern": "世界级(?:已完成|完成|已达成|达成|准备就绪|可声明|认证通过)" + } + ], + "scanned_surfaces": [ + { + "path": "README.md", + "violation_count": 0 + }, + { + "path": "SKILL.md", + "violation_count": 0 + }, + { + "path": "agents/interface.yaml", + "violation_count": 0 + }, + { + "path": "dist/manifest.json", + "violation_count": 0 + }, + { + "path": "dist/targets/claude/README.md", + "violation_count": 0 + }, + { + "path": "dist/targets/claude/adapter.json", + "violation_count": 0 + }, + { + "path": "dist/targets/generic/adapter.json", + "violation_count": 0 + }, + { + "path": "dist/targets/openai/adapter.json", + "violation_count": 0 + }, + { + "path": "dist/targets/vscode/README.md", + "violation_count": 0 + }, + { + "path": "dist/targets/vscode/adapter.json", + "violation_count": 0 + }, + { + "path": "docs/README.fr-FR.md", + "violation_count": 0 + }, + { + "path": "docs/README.ja-JP.md", + "violation_count": 0 + }, + { + "path": "docs/README.ru-RU.md", + "violation_count": 0 + }, + { + "path": "docs/README.zh-CN.md", + "violation_count": 0 + }, + { + "path": "docs/migration-v2.md", + "violation_count": 0 + }, + { + "path": "evidence/world_class/README.md", + "violation_count": 0 + }, + { + "path": "evidence/world_class/intake.schema.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/human-adjudication.intake.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/provider-holdout.intake.json", + "violation_count": 0 + }, + { + "path": "manifest.json", + "violation_count": 0 + }, + { + "path": "registry/examples/yao-meta-skill-1.0.0.json", + "violation_count": 0 + }, + { + "path": "registry/index.json", + "violation_count": 0 + }, + { + "path": "registry/index.schema.json", + "violation_count": 0 + }, + { + "path": "registry/package.schema.json", + "violation_count": 0 + }, + { + "path": "registry/packages/yao-meta-skill.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_approval_ledger.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_proposals.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_proposals.md", + "violation_count": 0 + }, + { + "path": "reports/adaptation_regression_report.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_regression_report.md", + "violation_count": 0 + }, + { + "path": "reports/adoption_drift_report.json", + "violation_count": 0 + }, + { + "path": "reports/adoption_drift_report.md", + "violation_count": 0 + }, + { + "path": "reports/architecture_maintainability.json", + "violation_count": 0 + }, + { + "path": "reports/architecture_maintainability.md", + "violation_count": 0 + }, + { + "path": "reports/artifact-design-profile.json", + "violation_count": 0 + }, + { + "path": "reports/artifact-design-profile.md", + "violation_count": 0 + }, + { + "path": "reports/baseline-compare.json", + "violation_count": 0 + }, + { + "path": "reports/baseline-compare.md", + "violation_count": 0 + }, + { + "path": "reports/benchmark_methodology.md", + "violation_count": 0 + }, + { + "path": "reports/benchmark_reproducibility.json", + "violation_count": 0 + }, + { + "path": "reports/benchmark_reproducibility.md", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.json", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/README.md", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/adjudication.md", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/blind-pack.json", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-01-support-triage.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-02-monthly-revenue-reconciliation.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-03-webinar-repurposing.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-04-incident-postmortem.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-05-pr-review-followup.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/index.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/review-decisions-template.json", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/review-decisions.recorded.json", + "violation_count": 0 + }, + { + "path": "reports/candidate_registry.json", + "violation_count": 0 + }, + { + "path": "reports/candidate_registry.md", + "violation_count": 0 + }, + { + "path": "reports/compiled_targets.json", + "violation_count": 0 + }, + { + "path": "reports/compiled_targets.md", + "violation_count": 0 + }, + { + "path": "reports/conformance_matrix.json", + "violation_count": 0 + }, + { + "path": "reports/conformance_matrix.md", + "violation_count": 0 + }, + { + "path": "reports/context_budget.json", + "violation_count": 0 + }, + { + "path": "reports/context_budget.md", + "violation_count": 0 + }, + { + "path": "reports/context_budget_summary.json", + "violation_count": 0 + }, + { + "path": "reports/description_drift_history.md", + "violation_count": 0 + }, + { + "path": "reports/description_optimization.json", + "violation_count": 0 + }, + { + "path": "reports/description_optimization.md", + "violation_count": 0 + }, + { + "path": "reports/description_optimization_suite.json", + "violation_count": 0 + }, + { + "path": "reports/description_optimization_suite.md", + "violation_count": 0 + }, + { + "path": "reports/eval_suite.json", + "violation_count": 0 + }, + { + "path": "reports/evidence_consistency.json", + "violation_count": 0 + }, + { + "path": "reports/evidence_consistency.md", + "violation_count": 0 + }, + { + "path": "reports/family_summary.md", + "violation_count": 0 + }, + { + "path": "reports/github-benchmark-scan.json", + "violation_count": 0 + }, + { + "path": "reports/github-benchmark-scan.md", + "violation_count": 0 + }, + { + "path": "reports/governance_score.json", + "violation_count": 0 + }, + { + "path": "reports/install_simulation.json", + "violation_count": 0 + }, + { + "path": "reports/install_simulation.md", + "violation_count": 0 + }, + { + "path": "reports/intent-confidence.json", + "violation_count": 0 + }, + { + "path": "reports/intent-confidence.md", + "violation_count": 0 + }, + { + "path": "reports/intent-context.json", + "violation_count": 0 + }, + { + "path": "reports/intent-dialogue.json", + "violation_count": 0 + }, + { + "path": "reports/intent-dialogue.md", + "violation_count": 0 + }, + { + "path": "reports/iteration-directions.json", + "violation_count": 0 + }, + { + "path": "reports/iteration-directions.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/governed-incident-command/bundle.json", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/governed-incident-command/bundle.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/governed-incident-command/review.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/team-frontend-review/bundle.json", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/team-frontend-review/bundle.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/team-frontend-review/review.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/yao-meta-skill/bundle.json", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/yao-meta-skill/bundle.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/yao-meta-skill/review.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_ledger.md", + "violation_count": 0 + }, + { + "path": "reports/output-risk-profile.json", + "violation_count": 0 + }, + { + "path": "reports/output-risk-profile.md", + "violation_count": 0 + }, + { + "path": "reports/output_blind_answer_key.json", + "violation_count": 0 + }, + { + "path": "reports/output_blind_review_pack.json", + "violation_count": 0 + }, + { + "path": "reports/output_blind_review_pack.md", + "violation_count": 0 + }, + { + "path": "reports/output_execution_runs.json", + "violation_count": 0 + }, + { + "path": "reports/output_execution_runs.md", + "violation_count": 0 + }, + { + "path": "reports/output_quality_scorecard.json", + "violation_count": 0 + }, + { + "path": "reports/output_quality_scorecard.md", + "violation_count": 0 + }, + { + "path": "reports/output_review_adjudication.json", + "violation_count": 0 + }, + { + "path": "reports/output_review_adjudication.md", + "violation_count": 0 + }, + { + "path": "reports/output_review_decisions.json", + "violation_count": 0 + }, + { + "path": "reports/output_review_kit.html", + "violation_count": 0 + }, + { + "path": "reports/output_review_kit.json", + "violation_count": 0 + }, + { + "path": "reports/output_review_kit.md", + "violation_count": 0 + }, + { + "path": "reports/package_verification.json", + "violation_count": 0 + }, + { + "path": "reports/package_verification.md", + "violation_count": 0 + }, + { + "path": "reports/portability_score.json", + "violation_count": 0 + }, + { + "path": "reports/portability_score.md", + "violation_count": 0 + }, + { + "path": "reports/promotion_decisions.json", + "violation_count": 0 + }, + { + "path": "reports/promotion_decisions.md", + "violation_count": 0 + }, + { + "path": "reports/prompt-quality-profile.json", + "violation_count": 0 + }, + { + "path": "reports/prompt-quality-profile.md", + "violation_count": 0 + }, + { + "path": "reports/python_compatibility.json", + "violation_count": 0 + }, + { + "path": "reports/python_compatibility.md", + "violation_count": 0 + }, + { + "path": "reports/reference-scan.json", + "violation_count": 0 + }, + { + "path": "reports/reference-scan.md", + "violation_count": 0 + }, + { + "path": "reports/reference-synthesis.json", + "violation_count": 0 + }, + { + "path": "reports/reference-synthesis.md", + "violation_count": 0 + }, + { + "path": "reports/registry_audit.json", + "violation_count": 0 + }, + { + "path": "reports/registry_audit.md", + "violation_count": 0 + }, + { + "path": "reports/regression_history.md", + "violation_count": 0 + }, + { + "path": "reports/review-studio.html", + "violation_count": 0 + }, + { + "path": "reports/review-studio.json", + "violation_count": 0 + }, + { + "path": "reports/review-viewer.html", + "violation_count": 0 + }, + { + "path": "reports/review-viewer.json", + "violation_count": 0 + }, + { + "path": "reports/review_annotations.json", + "violation_count": 0 + }, + { + "path": "reports/review_annotations.md", + "violation_count": 0 + }, + { + "path": "reports/review_waivers.json", + "violation_count": 0 + }, + { + "path": "reports/review_waivers.md", + "violation_count": 0 + }, + { + "path": "reports/route_scorecard.json", + "violation_count": 0 + }, + { + "path": "reports/route_scorecard.md", + "violation_count": 0 + }, + { + "path": "reports/runtime_permission_probes.json", + "violation_count": 0 + }, + { + "path": "reports/runtime_permission_probes.md", + "violation_count": 0 + }, + { + "path": "reports/security_trust_report.json", + "violation_count": 0 + }, + { + "path": "reports/security_trust_report.md", + "violation_count": 0 + }, + { + "path": "reports/skill-interpretation.html", + "violation_count": 0 + }, + { + "path": "reports/skill-interpretation.json", + "violation_count": 0 + }, + { + "path": "reports/skill-os-2-review.md", + "violation_count": 0 + }, + { + "path": "reports/skill-overview-v2-plan.md", + "violation_count": 0 + }, + { + "path": "reports/skill-overview.html", + "violation_count": 0 + }, + { + "path": "reports/skill-overview.json", + "violation_count": 0 + }, + { + "path": "reports/skill_atlas.html", + "violation_count": 0 + }, + { + "path": "reports/skill_atlas.json", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_audit.json", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_audit.md", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_coverage.json", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_coverage.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-16.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-16.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-17.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-17.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-20.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-20.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-21.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-21.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-07-02.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-07-02.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W25.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W25.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W27.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W27.md", + "violation_count": 0 + }, + { + "path": "reports/system-model.json", + "violation_count": 0 + }, + { + "path": "reports/system-model.md", + "violation_count": 0 + }, + { + "path": "reports/telemetry_hook_recipes.json", + "violation_count": 0 + }, + { + "path": "reports/telemetry_hook_recipes.md", + "violation_count": 0 + }, + { + "path": "reports/upgrade_check.json", + "violation_count": 0 + }, + { + "path": "reports/upgrade_check.md", + "violation_count": 0 + }, + { + "path": "reports/user_patterns.json", + "violation_count": 0 + }, + { + "path": "reports/user_patterns.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_claim_guard.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_intake.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_intake.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_ledger.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_ledger.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_plan.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_plan.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_preflight.html", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_preflight.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_preflight.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_operator_runbook.html", + "violation_count": 0 + }, + { + "path": "reports/world_class_operator_runbook.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_operator_runbook.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_submission_review.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_submission_review.md", + "violation_count": 0 + }, + { + "path": "security/dependency_policy.md", + "violation_count": 0 + }, + { + "path": "security/network_policy.json", + "violation_count": 0 + }, + { + "path": "security/network_policy.md", + "violation_count": 0 + }, + { + "path": "security/permission_policy.json", + "violation_count": 0 + }, + { + "path": "security/permission_policy.md", + "violation_count": 0 + }, + { + "path": "security/script_policy.md", + "violation_count": 0 + }, + { + "path": "security/secret_policy.md", + "violation_count": 0 + }, + { + "path": "security/trust_policy.md", + "violation_count": 0 + }, + { + "path": "skill-ir/examples/yao-meta-skill.json", + "violation_count": 0 + }, + { + "path": "skill-ir/schema.json", + "violation_count": 0 + } + ], + "violations": [], + "source_ledger": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md" + }, + "artifacts": { + "json": "reports/world_class_claim_guard.json", + "markdown": "reports/world_class_claim_guard.md" + } + }, + "registry": { + "ok": true, + "schema_version": "2.0", + "skill_dir": ".", + "registry_dir": "registry", + "package": { + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "agent-skills": "pass", + "vscode": "pass", + "generic": "pass", + "agent-skills-compatible": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "package_verification": "reports/package_verification.json", + "install_simulated": true, + "install_simulation": "reports/install_simulation.json" + }, + "generated_at": "2026-06-13" + }, + "index": { + "schema_version": "2.0", + "generated_at": "2026-06-13", + "package_count": 1, + "packages": [ + { + "name": "yao-meta-skill", + "version": "1.1.0", + "maturity": "governed", + "owner": "Yao Team", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "package_metadata": "registry/packages/yao-meta-skill.json", + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "failures": [], + "warnings": [], + "artifacts": { + "index": "registry/index.json", + "package": "registry/packages/yao-meta-skill.json", + "json": "reports/registry_audit.json", + "markdown": "reports/registry_audit.md" + } + }, + "package_verification": { + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-21", + "skill_dir": ".", + "package_dir": "dist", + "summary": { + "target_count": 4, + "adapter_count": 4, + "archive_present": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "archive_entry_count": 580, + "nested_skill_entry_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "checks": [ + { + "id": "package-manifest", + "status": "pass", + "detail": "Package manifest exists: dist/manifest.json" + }, + { + "id": "openai-adapter", + "status": "pass", + "detail": "Adapter exists for target: openai" + }, + { + "id": "openai-field-name", + "status": "pass", + "detail": "openai adapter includes field: name" + }, + { + "id": "openai-field-description", + "status": "pass", + "detail": "openai adapter includes field: description" + }, + { + "id": "openai-field-version", + "status": "pass", + "detail": "openai adapter includes field: version" + }, + { + "id": "openai-field-display_name", + "status": "pass", + "detail": "openai adapter includes field: display_name" + }, + { + "id": "openai-field-short_description", + "status": "pass", + "detail": "openai adapter includes field: short_description" + }, + { + "id": "openai-field-default_prompt", + "status": "pass", + "detail": "openai adapter includes field: default_prompt" + }, + { + "id": "openai-field-job_to_be_done", + "status": "pass", + "detail": "openai adapter includes field: job_to_be_done" + }, + { + "id": "openai-field-ir_source", + "status": "pass", + "detail": "openai adapter includes field: ir_source" + }, + { + "id": "openai-field-ir_schema_version", + "status": "pass", + "detail": "openai adapter includes field: ir_schema_version" + }, + { + "id": "openai-field-semantic_contract", + "status": "pass", + "detail": "openai adapter includes field: semantic_contract" + }, + { + "id": "openai-field-semantic_parity", + "status": "pass", + "detail": "openai adapter includes field: semantic_parity" + }, + { + "id": "openai-field-compiler", + "status": "pass", + "detail": "openai adapter includes field: compiler" + }, + { + "id": "openai-field-compiled_contract", + "status": "pass", + "detail": "openai adapter includes field: compiled_contract" + }, + { + "id": "openai-field-permission_contract", + "status": "pass", + "detail": "openai adapter includes field: permission_contract" + }, + { + "id": "openai-field-target_permission_contract", + "status": "pass", + "detail": "openai adapter includes field: target_permission_contract" + }, + { + "id": "openai-field-target_native_contract", + "status": "pass", + "detail": "openai adapter includes field: target_native_contract" + }, + { + "id": "openai-field-target_transform", + "status": "pass", + "detail": "openai adapter includes field: target_transform" + }, + { + "id": "openai-field-canonical_metadata", + "status": "pass", + "detail": "openai adapter includes field: canonical_metadata" + }, + { + "id": "openai-field-canonical_format", + "status": "pass", + "detail": "openai adapter includes field: canonical_format" + }, + { + "id": "openai-field-activation_mode", + "status": "pass", + "detail": "openai adapter includes field: activation_mode" + }, + { + "id": "openai-field-execution_context", + "status": "pass", + "detail": "openai adapter includes field: execution_context" + }, + { + "id": "openai-field-shell", + "status": "pass", + "detail": "openai adapter includes field: shell" + }, + { + "id": "openai-field-trust_level", + "status": "pass", + "detail": "openai adapter includes field: trust_level" + }, + { + "id": "openai-field-remote_inline_execution", + "status": "pass", + "detail": "openai adapter includes field: remote_inline_execution" + }, + { + "id": "openai-field-degradation_strategy", + "status": "pass", + "detail": "openai adapter includes field: degradation_strategy" + }, + { + "id": "openai-field-portability_profile", + "status": "pass", + "detail": "openai adapter includes field: portability_profile" + }, + { + "id": "claude-adapter", + "status": "pass", + "detail": "Adapter exists for target: claude" + }, + { + "id": "claude-field-name", + "status": "pass", + "detail": "claude adapter includes field: name" + }, + { + "id": "claude-field-description", + "status": "pass", + "detail": "claude adapter includes field: description" + }, + { + "id": "claude-field-version", + "status": "pass", + "detail": "claude adapter includes field: version" + }, + { + "id": "claude-field-display_name", + "status": "pass", + "detail": "claude adapter includes field: display_name" + }, + { + "id": "claude-field-short_description", + "status": "pass", + "detail": "claude adapter includes field: short_description" + }, + { + "id": "claude-field-default_prompt", + "status": "pass", + "detail": "claude adapter includes field: default_prompt" + }, + { + "id": "claude-field-job_to_be_done", + "status": "pass", + "detail": "claude adapter includes field: job_to_be_done" + }, + { + "id": "claude-field-ir_source", + "status": "pass", + "detail": "claude adapter includes field: ir_source" + }, + { + "id": "claude-field-ir_schema_version", + "status": "pass", + "detail": "claude adapter includes field: ir_schema_version" + }, + { + "id": "claude-field-semantic_contract", + "status": "pass", + "detail": "claude adapter includes field: semantic_contract" + }, + { + "id": "claude-field-semantic_parity", + "status": "pass", + "detail": "claude adapter includes field: semantic_parity" + }, + { + "id": "claude-field-compiler", + "status": "pass", + "detail": "claude adapter includes field: compiler" + }, + { + "id": "claude-field-compiled_contract", + "status": "pass", + "detail": "claude adapter includes field: compiled_contract" + }, + { + "id": "claude-field-permission_contract", + "status": "pass", + "detail": "claude adapter includes field: permission_contract" + }, + { + "id": "claude-field-target_permission_contract", + "status": "pass", + "detail": "claude adapter includes field: target_permission_contract" + }, + { + "id": "claude-field-target_native_contract", + "status": "pass", + "detail": "claude adapter includes field: target_native_contract" + }, + { + "id": "claude-field-target_transform", + "status": "pass", + "detail": "claude adapter includes field: target_transform" + }, + { + "id": "claude-field-canonical_metadata", + "status": "pass", + "detail": "claude adapter includes field: canonical_metadata" + }, + { + "id": "claude-field-canonical_format", + "status": "pass", + "detail": "claude adapter includes field: canonical_format" + }, + { + "id": "claude-field-activation_mode", + "status": "pass", + "detail": "claude adapter includes field: activation_mode" + }, + { + "id": "claude-field-execution_context", + "status": "pass", + "detail": "claude adapter includes field: execution_context" + }, + { + "id": "claude-field-shell", + "status": "pass", + "detail": "claude adapter includes field: shell" + }, + { + "id": "claude-field-trust_level", + "status": "pass", + "detail": "claude adapter includes field: trust_level" + }, + { + "id": "claude-field-remote_inline_execution", + "status": "pass", + "detail": "claude adapter includes field: remote_inline_execution" + }, + { + "id": "claude-field-degradation_strategy", + "status": "pass", + "detail": "claude adapter includes field: degradation_strategy" + }, + { + "id": "claude-field-portability_profile", + "status": "pass", + "detail": "claude adapter includes field: portability_profile" + }, + { + "id": "generic-adapter", + "status": "pass", + "detail": "Adapter exists for target: generic" + }, + { + "id": "generic-field-name", + "status": "pass", + "detail": "generic adapter includes field: name" + }, + { + "id": "generic-field-description", + "status": "pass", + "detail": "generic adapter includes field: description" + }, + { + "id": "generic-field-version", + "status": "pass", + "detail": "generic adapter includes field: version" + }, + { + "id": "generic-field-display_name", + "status": "pass", + "detail": "generic adapter includes field: display_name" + }, + { + "id": "generic-field-short_description", + "status": "pass", + "detail": "generic adapter includes field: short_description" + }, + { + "id": "generic-field-default_prompt", + "status": "pass", + "detail": "generic adapter includes field: default_prompt" + }, + { + "id": "generic-field-job_to_be_done", + "status": "pass", + "detail": "generic adapter includes field: job_to_be_done" + }, + { + "id": "generic-field-ir_source", + "status": "pass", + "detail": "generic adapter includes field: ir_source" + }, + { + "id": "generic-field-ir_schema_version", + "status": "pass", + "detail": "generic adapter includes field: ir_schema_version" + }, + { + "id": "generic-field-semantic_contract", + "status": "pass", + "detail": "generic adapter includes field: semantic_contract" + }, + { + "id": "generic-field-semantic_parity", + "status": "pass", + "detail": "generic adapter includes field: semantic_parity" + }, + { + "id": "generic-field-compiler", + "status": "pass", + "detail": "generic adapter includes field: compiler" + }, + { + "id": "generic-field-compiled_contract", + "status": "pass", + "detail": "generic adapter includes field: compiled_contract" + }, + { + "id": "generic-field-permission_contract", + "status": "pass", + "detail": "generic adapter includes field: permission_contract" + }, + { + "id": "generic-field-target_permission_contract", + "status": "pass", + "detail": "generic adapter includes field: target_permission_contract" + }, + { + "id": "generic-field-target_native_contract", + "status": "pass", + "detail": "generic adapter includes field: target_native_contract" + }, + { + "id": "generic-field-target_transform", + "status": "pass", + "detail": "generic adapter includes field: target_transform" + }, + { + "id": "generic-field-canonical_metadata", + "status": "pass", + "detail": "generic adapter includes field: canonical_metadata" + }, + { + "id": "generic-field-canonical_format", + "status": "pass", + "detail": "generic adapter includes field: canonical_format" + }, + { + "id": "generic-field-activation_mode", + "status": "pass", + "detail": "generic adapter includes field: activation_mode" + }, + { + "id": "generic-field-execution_context", + "status": "pass", + "detail": "generic adapter includes field: execution_context" + }, + { + "id": "generic-field-shell", + "status": "pass", + "detail": "generic adapter includes field: shell" + }, + { + "id": "generic-field-trust_level", + "status": "pass", + "detail": "generic adapter includes field: trust_level" + }, + { + "id": "generic-field-remote_inline_execution", + "status": "pass", + "detail": "generic adapter includes field: remote_inline_execution" + }, + { + "id": "generic-field-degradation_strategy", + "status": "pass", + "detail": "generic adapter includes field: degradation_strategy" + }, + { + "id": "generic-field-portability_profile", + "status": "pass", + "detail": "generic adapter includes field: portability_profile" + }, + { + "id": "vscode-adapter", + "status": "pass", + "detail": "Adapter exists for target: vscode" + }, + { + "id": "vscode-field-name", + "status": "pass", + "detail": "vscode adapter includes field: name" + }, + { + "id": "vscode-field-description", + "status": "pass", + "detail": "vscode adapter includes field: description" + }, + { + "id": "vscode-field-version", + "status": "pass", + "detail": "vscode adapter includes field: version" + }, + { + "id": "vscode-field-display_name", + "status": "pass", + "detail": "vscode adapter includes field: display_name" + }, + { + "id": "vscode-field-short_description", + "status": "pass", + "detail": "vscode adapter includes field: short_description" + }, + { + "id": "vscode-field-default_prompt", + "status": "pass", + "detail": "vscode adapter includes field: default_prompt" + }, + { + "id": "vscode-field-job_to_be_done", + "status": "pass", + "detail": "vscode adapter includes field: job_to_be_done" + }, + { + "id": "vscode-field-ir_source", + "status": "pass", + "detail": "vscode adapter includes field: ir_source" + }, + { + "id": "vscode-field-ir_schema_version", + "status": "pass", + "detail": "vscode adapter includes field: ir_schema_version" + }, + { + "id": "vscode-field-semantic_contract", + "status": "pass", + "detail": "vscode adapter includes field: semantic_contract" + }, + { + "id": "vscode-field-semantic_parity", + "status": "pass", + "detail": "vscode adapter includes field: semantic_parity" + }, + { + "id": "vscode-field-compiler", + "status": "pass", + "detail": "vscode adapter includes field: compiler" + }, + { + "id": "vscode-field-compiled_contract", + "status": "pass", + "detail": "vscode adapter includes field: compiled_contract" + }, + { + "id": "vscode-field-permission_contract", + "status": "pass", + "detail": "vscode adapter includes field: permission_contract" + }, + { + "id": "vscode-field-target_permission_contract", + "status": "pass", + "detail": "vscode adapter includes field: target_permission_contract" + }, + { + "id": "vscode-field-target_native_contract", + "status": "pass", + "detail": "vscode adapter includes field: target_native_contract" + }, + { + "id": "vscode-field-target_transform", + "status": "pass", + "detail": "vscode adapter includes field: target_transform" + }, + { + "id": "vscode-field-canonical_metadata", + "status": "pass", + "detail": "vscode adapter includes field: canonical_metadata" + }, + { + "id": "vscode-field-canonical_format", + "status": "pass", + "detail": "vscode adapter includes field: canonical_format" + }, + { + "id": "vscode-field-activation_mode", + "status": "pass", + "detail": "vscode adapter includes field: activation_mode" + }, + { + "id": "vscode-field-execution_context", + "status": "pass", + "detail": "vscode adapter includes field: execution_context" + }, + { + "id": "vscode-field-shell", + "status": "pass", + "detail": "vscode adapter includes field: shell" + }, + { + "id": "vscode-field-trust_level", + "status": "pass", + "detail": "vscode adapter includes field: trust_level" + }, + { + "id": "vscode-field-remote_inline_execution", + "status": "pass", + "detail": "vscode adapter includes field: remote_inline_execution" + }, + { + "id": "vscode-field-degradation_strategy", + "status": "pass", + "detail": "vscode adapter includes field: degradation_strategy" + }, + { + "id": "vscode-field-portability_profile", + "status": "pass", + "detail": "vscode adapter includes field: portability_profile" + }, + { + "id": "openai-file-targets/openai/adapter.json", + "status": "pass", + "detail": "Package contains targets/openai/adapter.json" + }, + { + "id": "openai-file-targets/openai/agents/openai.yaml", + "status": "pass", + "detail": "Package contains targets/openai/agents/openai.yaml" + }, + { + "id": "claude-file-targets/claude/adapter.json", + "status": "pass", + "detail": "Package contains targets/claude/adapter.json" + }, + { + "id": "claude-file-targets/claude/README.md", + "status": "pass", + "detail": "Package contains targets/claude/README.md" + }, + { + "id": "generic-file-targets/generic/adapter.json", + "status": "pass", + "detail": "Package contains targets/generic/adapter.json" + }, + { + "id": "vscode-file-targets/vscode/adapter.json", + "status": "pass", + "detail": "Package contains targets/vscode/adapter.json" + }, + { + "id": "vscode-file-targets/vscode/README.md", + "status": "pass", + "detail": "Package contains targets/vscode/README.md" + }, + { + "id": "archive-safe-paths", + "status": "pass", + "detail": "Archive has no absolute or parent-traversal entries" + }, + { + "id": "archive-entry-yao-meta-skill/SKILL.md", + "status": "pass", + "detail": "Archive contains yao-meta-skill/SKILL.md" + }, + { + "id": "archive-entry-yao-meta-skill/manifest.json", + "status": "pass", + "detail": "Archive contains yao-meta-skill/manifest.json" + }, + { + "id": "archive-entry-yao-meta-skill/agents/interface.yaml", + "status": "pass", + "detail": "Archive contains yao-meta-skill/agents/interface.yaml" + }, + { + "id": "archive-single-skill-entrypoint", + "status": "pass", + "detail": "Archive exposes only the root SKILL.md entrypoint" + }, + { + "id": "archive-excludes-generated", + "status": "pass", + "detail": "Archive excludes generated dist/, .previews/, and tests/tmp* contents" + }, + { + "id": "registry-ok", + "status": "pass", + "detail": "Registry audit is OK" + }, + { + "id": "registry-name-match", + "status": "pass", + "detail": "Registry package name matches package manifest" + }, + { + "id": "registry-version-match", + "status": "pass", + "detail": "Registry package version matches package manifest" + }, + { + "id": "registry-compat-openai", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: openai" + }, + { + "id": "registry-compat-claude", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: claude" + }, + { + "id": "registry-compat-generic", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: generic" + }, + { + "id": "registry-compat-vscode", + "status": "pass", + "detail": "Registry compatibility is reviewable for target: vscode" + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "manifest": "dist/manifest.json", + "archive": "dist/yao-meta-skill.zip", + "json": "reports/package_verification.json", + "markdown": "reports/package_verification.md" + } + }, + "install_simulation": { + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-13", + "skill_dir": ".", + "package_dir": "tests/tmp_review_studio/dist", + "install_root": "tests/tmp_review_studio/install-root/simulate-yao-meta-skill", + "installed_skill_dir": "tests/tmp_review_studio/install-root/simulate-yao-meta-skill/yao-meta-skill", + "summary": { + "archive_present": true, + "archive_entry_count": 598, + "nested_skill_entry_count": 0, + "archive_extracted": true, + "entrypoint_loaded": true, + "manifest_loaded": true, + "interface_loaded": true, + "adapter_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "install_root_is_temp": false, + "failure_count": 0, + "warning_count": 0 + }, + "checks": [ + { + "id": "archive-present", + "status": "pass", + "detail": "Package archive exists: tests/tmp_review_studio/dist/yao-meta-skill.zip" + }, + { + "id": "archive-safe-paths", + "status": "pass", + "detail": "Archive has no absolute or parent-traversal entries" + }, + { + "id": "single-skill-entrypoint", + "status": "pass", + "detail": "Installed package exposes only the root SKILL.md entrypoint" + }, + { + "id": "single-top-level", + "status": "pass", + "detail": "Archive top-level directory is yao-meta-skill" + }, + { + "id": "entrypoint-load", + "status": "pass", + "detail": "Installed SKILL.md frontmatter is readable" + }, + { + "id": "entrypoint-name", + "status": "pass", + "detail": "Installed SKILL.md name matches package directory" + }, + { + "id": "entrypoint-description", + "status": "pass", + "detail": "Installed SKILL.md description is present" + }, + { + "id": "manifest-load", + "status": "pass", + "detail": "Installed manifest.json is readable" + }, + { + "id": "manifest-name", + "status": "pass", + "detail": "Installed manifest name matches package manifest" + }, + { + "id": "manifest-version", + "status": "pass", + "detail": "Installed manifest version matches package manifest" + }, + { + "id": "interface-load", + "status": "pass", + "detail": "Installed agents/interface.yaml is readable" + }, + { + "id": "overview-report", + "status": "pass", + "detail": "Installed overview report is present" + }, + { + "id": "review-studio-report", + "status": "pass", + "detail": "Installed Review Studio report is present" + }, + { + "id": "adapter-claude", + "status": "pass", + "detail": "claude adapter is readable after package install simulation" + }, + { + "id": "adapter-claude-name", + "status": "pass", + "detail": "claude adapter name matches package manifest" + }, + { + "id": "adapter-generic", + "status": "pass", + "detail": "generic adapter is readable after package install simulation" + }, + { + "id": "adapter-generic-name", + "status": "pass", + "detail": "generic adapter name matches package manifest" + }, + { + "id": "adapter-openai", + "status": "pass", + "detail": "openai adapter is readable after package install simulation" + }, + { + "id": "adapter-openai-name", + "status": "pass", + "detail": "openai adapter name matches package manifest" + }, + { + "id": "adapter-vscode", + "status": "pass", + "detail": "vscode adapter is readable after package install simulation" + }, + { + "id": "adapter-vscode-name", + "status": "pass", + "detail": "vscode adapter name matches package manifest" + }, + { + "id": "permission-policy-load", + "status": "pass", + "detail": "Installed permission policy is readable" + }, + { + "id": "permission-claude-contract", + "status": "pass", + "detail": "claude adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-claude-file_write-approved", + "status": "pass", + "detail": "claude capability file_write has active reviewer approval" + }, + { + "id": "permission-claude-file_write-target-enforcement", + "status": "pass", + "detail": "claude capability file_write has target enforcement note" + }, + { + "id": "permission-claude-network-approved", + "status": "pass", + "detail": "claude capability network has active reviewer approval" + }, + { + "id": "permission-claude-network-target-enforcement", + "status": "pass", + "detail": "claude capability network has target enforcement note" + }, + { + "id": "permission-claude-subprocess-approved", + "status": "pass", + "detail": "claude capability subprocess has active reviewer approval" + }, + { + "id": "permission-claude-subprocess-target-enforcement", + "status": "pass", + "detail": "claude capability subprocess has target enforcement note" + }, + { + "id": "permission-generic-contract", + "status": "pass", + "detail": "generic adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-generic-file_write-approved", + "status": "pass", + "detail": "generic capability file_write has active reviewer approval" + }, + { + "id": "permission-generic-file_write-target-enforcement", + "status": "pass", + "detail": "generic capability file_write has target enforcement note" + }, + { + "id": "permission-generic-network-approved", + "status": "pass", + "detail": "generic capability network has active reviewer approval" + }, + { + "id": "permission-generic-network-target-enforcement", + "status": "pass", + "detail": "generic capability network has target enforcement note" + }, + { + "id": "permission-generic-subprocess-approved", + "status": "pass", + "detail": "generic capability subprocess has active reviewer approval" + }, + { + "id": "permission-generic-subprocess-target-enforcement", + "status": "pass", + "detail": "generic capability subprocess has target enforcement note" + }, + { + "id": "permission-openai-contract", + "status": "pass", + "detail": "openai adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-openai-file_write-approved", + "status": "pass", + "detail": "openai capability file_write has active reviewer approval" + }, + { + "id": "permission-openai-file_write-target-enforcement", + "status": "pass", + "detail": "openai capability file_write has target enforcement note" + }, + { + "id": "permission-openai-network-approved", + "status": "pass", + "detail": "openai capability network has active reviewer approval" + }, + { + "id": "permission-openai-network-target-enforcement", + "status": "pass", + "detail": "openai capability network has target enforcement note" + }, + { + "id": "permission-openai-subprocess-approved", + "status": "pass", + "detail": "openai capability subprocess has active reviewer approval" + }, + { + "id": "permission-openai-subprocess-target-enforcement", + "status": "pass", + "detail": "openai capability subprocess has target enforcement note" + }, + { + "id": "permission-vscode-contract", + "status": "pass", + "detail": "vscode adapter exposes target permission contract for installer enforcement" + }, + { + "id": "permission-vscode-file_write-approved", + "status": "pass", + "detail": "vscode capability file_write has active reviewer approval" + }, + { + "id": "permission-vscode-file_write-target-enforcement", + "status": "pass", + "detail": "vscode capability file_write has target enforcement note" + }, + { + "id": "permission-vscode-network-approved", + "status": "pass", + "detail": "vscode capability network has active reviewer approval" + }, + { + "id": "permission-vscode-network-target-enforcement", + "status": "pass", + "detail": "vscode capability network has target enforcement note" + }, + { + "id": "permission-vscode-subprocess-approved", + "status": "pass", + "detail": "vscode capability subprocess has active reviewer approval" + }, + { + "id": "permission-vscode-subprocess-target-enforcement", + "status": "pass", + "detail": "vscode capability subprocess has target enforcement note" + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "archive": "tests/tmp_review_studio/dist/yao-meta-skill.zip", + "package_manifest": "tests/tmp_review_studio/dist/manifest.json", + "json": "tests/tmp_review_studio/install_simulation.json", + "markdown": "tests/tmp_review_studio/install_simulation.md" + } + }, + "upgrade_check": { + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-13", + "previous": { + "name": "yao-meta-skill", + "version": "1.0.0", + "targets": [ + "openai", + "claude", + "generic" + ] + }, + "current": { + "name": "yao-meta-skill", + "version": "1.1.0", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "summary": { + "declared_bump": "minor", + "recommended_bump": "minor", + "breaking_change_count": 0, + "added_target_count": 2, + "metadata_change_count": 1, + "checksum_change_count": 2, + "failure_count": 0, + "warning_count": 0 + }, + "upgrade_diff": { + "name_changed": false, + "added_targets": [ + "agent-skills-compatible", + "vscode" + ], + "removed_targets": [], + "compatibility_changes": [ + { + "target": "agent-skills", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "agent-skills-compatible", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "vscode", + "from": "missing", + "to": "pass", + "direction": "improved" + } + ], + "metadata_changes": [ + { + "field": "description", + "from": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes.", + "to": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + } + ], + "checksum_changes": [ + { + "field": "archive_sha256", + "from": "", + "to": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + { + "field": "package_sha256", + "from": "0000000000000000000000000000000000000000000000000000000000000000", + "to": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "release_notes": [ + "Recommended version bump: minor.", + "Added targets: agent-skills-compatible, vscode.", + "Package or archive checksum changed; reviewers should verify package artifacts before release.", + "Package verification evidence: reports/package_verification.md." + ], + "failures": [], + "warnings": [], + "artifacts": { + "previous_package": "registry/examples/yao-meta-skill-1.0.0.json", + "current_package": "reports/registry_audit.json", + "json": "tests/tmp_review_studio/upgrade_check.json", + "markdown": "tests/tmp_review_studio/upgrade_check.md" + } + }, + "manifest": { + "name": "yao-meta-skill", + "version": "1.1.0", + "owner": "Yao Team", + "updated_at": "2026-03-31", + "status": "active", + "maturity_tier": "governed", + "lifecycle_stage": "library", + "context_budget_tier": "production", + "review_cadence": "quarterly", + "target_platforms": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "factory_components": [ + "templates", + "references", + "scripts", + "reports" + ] + }, + "frontmatter": { + "name": "yao-meta-skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "metadata": { + "author": "Yao Team" + } + }, + "interface": { + "interface": { + "display_name": "Yao Meta Skill", + "short_description": "Create trigger-aware agent skills", + "default_prompt": "Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals." + }, + "compatibility": { + "canonical_format": "agent-skills", + "adapter_targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "activation": { + "mode": "manual", + "paths": [] + }, + "execution": { + "context": "inline", + "shell": "bash" + }, + "trust": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "degradation": { + "openai": "metadata-adapter", + "claude": "neutral-source-plus-adapter", + "generic": "neutral-source", + "vscode": "agent-skills-source-with-vscode-notes" + } + } + } + }, + "artifacts": { + "html": "reports/review-studio.html", + "json": "reports/review-studio.json" + } +} diff --git a/reports/review-viewer.html b/reports/review-viewer.html new file mode 100644 index 0000000..1f94f65 --- /dev/null +++ b/reports/review-viewer.html @@ -0,0 +1,376 @@ + + + + + + Yao Meta Skill Review Viewer + + + +
    +
    +

    Review Viewer

    +

    Yao Meta Skill

    +

    Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.

    +
    + maturity: governed + archetype: governed + format: agent-skills + updated: 2026-03-31 + intent confidence: 100 / 100 +
    +
    + +
    +

    Architecture at a glance

    +
    Inputs
    workflow, prompt, transcript, docs, or notes
    Boundary
    Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.
    Logic
    One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.; Capture job, output, exclusions, constraints, standards, lightest fit.; Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.
    Usage
    Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.; Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.
    Next
    Review the top iteration directions before growing the package.
    +
    + +
    +
    +

    Core logic

    +
    • One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.
    • Capture job, output, exclusions, constraints, standards, lightest fit.
    • Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.
    • Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.
    • Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned.
    +
    +
    +

    How to use it

    +
    • Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.
    • Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.
    +
    +
    + +
    +
    +

    Intent questions

    +
    • If this skill worked beautifully, what recurring job would it reliably handle for the user every time?
      This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape.
    • When someone reaches for this skill in the real world, what materials will they actually hand to it?
      Input shape decides whether references, scripts, or templates are needed.
    • What finished output should it hand back so the user can immediately keep moving?
      Outputs should drive the package structure before extra guidance is added.
    • Which nearby requests should this skill politely refuse so the boundary stays clean?
      The exclusion list is the fastest route to better trigger quality.
    • What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?
      Constraints decide how much structure, packaging, and review the skill actually needs.
    +
    +
    +

    Why this package is strong

    +
    • 触发面保持精简,并锚定在 frontmatter description。
    • 已生成 Skill IR,核心语义可先于平台打包被审查和迁移。
    • 已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。
    • 已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。
    • 已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。
    • 已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。
    +
    +
    + +
    +
    +

    Borrow plan

    +
    • No external benchmark objects recorded yet. Add 2 to 5 references before deepening the package.
    +
    +
    +

    Compare view

    +

    Winner: Current

    {'priority': ['fewest false positives', 'fewest false negatives', 'highest near-neighbor pass rate', 'highest negative pass rate', 'highest precision', 'highest recall', 'shortest description']}

    VariantTokensDev ErrorsHoldout ErrorsStrategy
    Baseline800existing
    Current5400current
    Current5400current
    +
    +
    + +
    +

    Variant diff studio

    +

    Baseline

    existing

    Create and improve agent skills.

    tokens 8 (0)dev 0 (0)holdout 0 (0)

    Adds relative to baseline

    • Create and improve agent skills.

    Drops from baseline

    No dropped cues.

    Current

    current

    Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.

    tokens 54 (+46)dev 0 (0)holdout 0 (0)

    Adds relative to baseline

    • Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts.
    • Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测.
    • Exclude no-skill summary/translation/docs.

    Drops from baseline

    • Create and improve agent skills.

    Winner — Current

    current

    Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.

    tokens 54 (+46)dev 0 (0)holdout 0 (0)

    Adds relative to baseline

    • Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts.
    • Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测.
    • Exclude no-skill summary/translation/docs.

    Drops from baseline

    • Create and improve agent skills.
    +
    + +
    +
    +

    Evidence readiness

    +

    Readiness score: 85/100

    +
    • Intent clarity · ready
      100/100 intent confidence.
    • Benchmark coverage · ready
      3 GitHub benchmark repositories attached.
    • Pattern gate · ready
      5 accepted, 1 deferred using threshold 4/4.
    • Conflict handling · decision needed
      The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.
    • Output risk profile · ready
      5 output risk families attached.
    • Artifact design profile · ready
      Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.
    • Prompt quality profile · ready
      89.0/100 prompt-facing quality score.
    +
    +
    +

    Honest boundary check

    +
      +
    • Are the known limits visible before the package deepens?
    • +
    • Does the evidence support the borrowed patterns?
    • +
    • Should uncertainty become a clarification question instead of more structure?
    • +
    +
    +
    + +
    +
    +

    Output risk profile

    +
    • Markdown readability
      Tables can render as dense grids with weak hierarchy or poor mobile readability.; Long bullets can make the output look complete while hiding the actual decision logic.
    • Citation and footnote clutter
      Footnote markers or dense citation notes can interrupt the reading flow.; Evidence can be over-attached to obvious statements and under-attached to risky claims.
    • Screenshot and visual capture
      Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.; Missing screenshots can cause the skill to invent visual references instead of declaring the gap.
    +
    +
    +

    Self-repair checks

    +
    • Preview whether each table still reads well when columns are narrow.
    • Convert any table with paragraph-length cells into bullets or cards.
    • Remove decorative citations that do not support a material claim.
    • Move repeated source explanations into one compact source note.
    • Check that every screenshot reference points to a real provided or generated asset.
    +
    +
    + +
    +
    +

    Artifact design profile

    +

    Design system: metric editorial

    +
    • Dashboard or metrics page
      Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.
    • Review viewer
      Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix.
    • Code, CLI, or implementation guide
      Execution-focused technical artifact with environment assumptions, copyable commands, expected outputs, and side effects made explicit.
    +
    +
    +

    Visual quality gates

    +
    • Avoid paragraph-heavy table cells.
    • Keep charts tied to one analytical question each.
    • Preserve stable color meaning across metrics and entities.
    • Make differences visible instead of hiding them in prose.
    • Separate author-facing recommendations from reviewer-only evidence.
    +
    +
    + +
    +
    +

    Prompt quality profile

    +

    Relevance: prompt-aware · score 89.0 / 100 · complexity expert

    +
    • Completeness · 100 / 100
      Name missing inputs, outputs, constraints, or success standards before deepening the package.
    • Clarity · 85 / 100
      Replace broad verbs with observable actions and define what done means.
    • Consistency · 90 / 100
      Check that role, task, format, exclusions, and examples do not contradict each other.
    • Practicality · 90 / 100
      Add runnable steps, examples, or verification cues instead of abstract advice.
    • Specificity · 80 / 100
      Anchor wording in the user's audience, domain nouns, and target outcome.
    +
    +
    +

    RTF to skill mapping

    +
    • Role
      Use an operator role with explicit boundaries, inputs, outputs, and failure handling.
    • Task
      Convert the job into ordered steps with validation checks and stop conditions.
    • Format
      Return a runbook-like handoff with commands, checks, owners, and next actions when relevant.
    +
    +
    + +
    +
    +

    Reference coach

    +

    obra/superpowers

    Borrow now

    • Borrow the way it turns a messy workflow into a repeatable operating path.
    • Borrow the clear execution entrypoints and command structure.

    Avoid

    • Do not import process overhead that only exists for that project's scale.

    affaan-m/ECC

    Borrow now

    • Borrow the way it turns a messy workflow into a repeatable operating path.
    • Borrow the clear execution entrypoints and command structure.

    Avoid

    • Do not import process overhead that only exists for that project's scale.

    multica-ai/andrej-karpathy-skills

    Borrow now

    • Borrow explicit validation and quality gates that make iteration safer.
    • Borrow the way it separates explanation, examples, and reusable structure.

    Avoid

    • Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.
    +
    +
    +

    Decide before you deepen

    +
      +
    • Choose one pattern to borrow on purpose, not three at once.
    • +
    • State one thing this skill will not inherit from the benchmark objects.
    • +
    • Only deepen the package after that choice is visible in the boundary or execution flow.
    • +
    +
    +
    + +
    +
    +

    Reference synthesis

    +

    Official skill anatomy and context discipline

    Borrow now

    • Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.

    Avoid

    • Do not let packaging or platform concerns swallow the core job boundary.

    Hypothesis-test-learn loop

    Borrow now

    • Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.

    Avoid

    • Do not create experimental overhead that exceeds the skill's real risk tier.

    Boundary-first design

    Borrow now

    • Borrow the discipline of defining what the skill should not own before growing the package.

    Avoid

    • Do not expand execution assets until route boundaries stay clean.
    +
    +
    +

    Borrow now

    +
    • Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.
    • Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.
    • Borrow the discipline of defining what the skill should not own before growing the package.
    • Borrow the way it turns a messy workflow into a repeatable operating path.
    +

    Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call.

    +
    +
    + +
    +

    Top three next moves

    +

    Borrow one proven pattern on purpose

    You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.

    • Read the strongest pattern from obra/superpowers.
    • Decide whether to borrow method, structure, execution, or portability, but only one of them first.
    • Record what you will not borrow so the package stays light.
    Unlocks: A cleaner package shape with less accidental over-design.

    Harden portability semantics

    The skill already signals reuse across environments, so contract clarity matters early.

    • Confirm activation mode, execution context, and trust assumptions.
    • Add or review degradation strategy for non-native targets.
    • Package the skill once to verify adapter expectations.
    Unlocks: Safer cross-environment reuse with less target drift.

    Create an iteration evidence loop

    The package should show what changed and why after the first draft.

    • Generate the HTML skill report and keep it aligned with the package.
    • Record reference scan choices and non-goals.
    • Capture the next iteration choice explicitly before adding more files.
    Unlocks: A clearer path for the next author or reviewer.
    +
    + +
    +
    +

    Recent feedback

    +
    • No lightweight feedback captured yet. Use `yao.py feedback` to record quick review notes.
    +
    +
    +

    Promotion status

    +

    Promote: 0

    Keep current: 3

    Blocked: 0

    +
    +
    + +
    +
    +

    Package map

    +
    • SKILL.md — Skill entrypoint
    • README.md — Human-readable usage guide
    • agents/interface.yaml — Neutral interface metadata
    • manifest.json — Lifecycle and portability metadata
    • references — Extended guidance and reusable notes
    • scripts — Deterministic helpers or local tooling
    • evals — Trigger and quality checks
    • reports — Generated evidence and overview artifacts
    +
    +
    +

    First-pass review frame

    +
      +
    • Does the trigger stay narrow enough for the intended job?
    • +
    • Does the archetype match the real reuse level?
    • +
    • Are we adding structure faster than we are adding reliability?
    • +
    • Should the next step be trigger tightening, execution assets, or portability hardening?
    • +
    +
    +
    + +
    +
    +

    Authoring discipline

    +
      +
    • Name unresolved assumptions before deepening the package.
    • +
    • Keep the package no larger than the recurring job requires.
    • +
    • Touch only files that directly support the requested change.
    • +
    • Tie every meaningful new artifact to a check or reviewer note.
    • +
    +
    +
    +

    Reviewer guardrails

    +
      +
    • Block speculative features that are not backed by real workflow variation.
    • +
    • Move unverifiable ideas into next-step candidates instead of baseline structure.
    • +
    • Reject decorative folders, reports, or governance that do not reduce risk.
    • +
    • Ask for one high-leverage clarification when job, output, or exclusion is still fuzzy.
    • +
    +
    +
    +
    + + diff --git a/reports/review-viewer.json b/reports/review-viewer.json new file mode 100644 index 0000000..71b7296 --- /dev/null +++ b/reports/review-viewer.json @@ -0,0 +1,14811 @@ +{ + "overview": { + "skill_summary": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "maturity": "governed", + "updated_at": "2026-03-31", + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": [ + "SKILL.md", + "agents/interface.yaml", + "skill-ir/examples/yao-meta-skill.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_adjudication.md", + "reports/benchmark_reproducibility.md", + "reports/output_blind_answer_key.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/world_class_evidence_plan.md", + "reports/world_class_evidence_ledger.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-interpretation.html", + "reports/skill-overview.html" + ], + "flow": [ + "输入材料", + "Skill 包体", + "可复用能力" + ] + }, + "scorecard": { + "completeness_score": { + "label": "完整度", + "score": 100, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 100, + "reasons": [ + "已生成 20 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 100, + "reasons": [ + "SKILL.md 约 314 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 已承载确定性逻辑。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 42, + "reasons": [ + "入口约 314 个词/字,references 约 16934 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本偏高,建议压缩入口或拆分 references。" + ] + } + }, + "capability_profile": { + "archetype": "governed", + "task_family": "Execution operation", + "maturity": "governed", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "principle_model": { + "nodes": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "contract_boundary": { + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "quality_review": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "gaps": [ + "上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + } + }, + "risk_governance": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 1, + "signal": "SKILL.md 约 314 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "world_class_readiness": { + "ready": false, + "decision": "evidence-pending", + "entry_count": 4, + "pending_count": 4, + "accepted_count": 0, + "external_pending_count": 3, + "human_pending_count": 1, + "source_check_count": 19, + "source_pass_count": 12, + "conclusion_zh": "世界级证据尚未完成:4 项待补,0 项已接受。", + "conclusion_en": "World-class evidence is not complete: 4 pending, 0 accepted.", + "entries": [ + { + "key": "provider-holdout", + "label_zh": "提供商留出", + "label_en": "Provider Holdout", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "缺少真实 provider 模型运行和 token metadata。", + "summary_en": "Missing a real provider model run and token metadata.", + "blocked_checks": [] + }, + { + "key": "human-adjudication", + "label_zh": "人工盲评", + "label_en": "Human Adjudication", + "category": "human", + "category_zh": "人工证据", + "category_en": "Human evidence", + "status": "pending", + "summary_zh": "盲评 pair 仍待真实 reviewer 决策。", + "summary_en": "Blind-review pairs still need real reviewer decisions.", + "blocked_checks": [ + "No pending decisions", + "Judgments complete", + "Reviewer metadata" + ] + }, + { + "key": "native-permission-enforcement", + "label_zh": "原生权限", + "label_en": "Native Permission", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "原生 runtime enforcement 仍待目标客户端或外部安装器证明。", + "summary_en": "Native runtime enforcement still needs target-client or external-installer proof.", + "blocked_checks": [ + "Native enforcement" + ] + }, + { + "key": "native-client-telemetry", + "label_zh": "原生遥测", + "label_en": "Native Telemetry", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "真实外部客户端 metadata-only 事件仍未导入。", + "summary_en": "Real external-client metadata-only events have not been imported yet.", + "blocked_checks": [ + "External events" + ] + } + ] + }, + "package_assets": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "file_count": 393, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 35 + }, + { + "label": "scripts", + "value": 155 + }, + { + "label": "evals", + "value": 29 + }, + { + "label": "reports", + "value": 170 + } + ] + }, + "iteration_roadmap": { + "items": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ] + }, + "report_contract": { + "schema_version": "2.0", + "html_report": "reports/skill-overview.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "layout": "kami-white-audit-v2", + "nav_labels": [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线" + ], + "nav_labels_en": [ + "Overview", + "Metrics", + "Profile", + "Principle", + "Contract", + "Quality", + "Risk", + "Assets", + "Roadmap" + ] + }, + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "logic_steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "usage_steps": [ + "Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.", + "Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + ], + "package_map": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "scenarios": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals." + ], + "trigger_contract": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "io_contract": { + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ] + }, + "principles": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "roadmap": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ], + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。" + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": "2.0.0", + "source_path": "skill-ir/examples/yao-meta-skill.json", + "target_count": 5, + "trigger_samples": 8, + "output_eval_cases": 3 + }, + "compiled_targets": { + "ok": true, + "schema_version": "1.0", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter_mode": "metadata-adapter", + "degradation_strategy": "metadata-adapter", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "permission_enforcement": "metadata-only", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "adapter_mode": "neutral-source-plus-adapter", + "degradation_strategy": "neutral-source-plus-adapter", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "permission_enforcement": "metadata-fallback", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [] + }, + { + "target": "generic", + "status": "pass", + "adapter_mode": "agent-skills-compatible", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills compatible neutral package", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "targets/generic/adapter.json" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "agent-skills-compatible", + "status": "pass", + "adapter_mode": "neutral-agent-skills-source", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills standard source tree", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "vscode", + "status": "pass", + "adapter_mode": "vscode-agent-skills-adapter", + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "permission_enforcement": "client-or-workspace-trust", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [] + } + ], + "failures": [], + "warnings": [] + }, + "output_quality": { + "case_count": 5, + "file_backed_case_count": 1, + "near_neighbor_case_count": 1, + "boundary_case_count": 1, + "baseline_pass_rate": 0.0, + "with_skill_pass_rate": 100.0, + "delta": 100.0, + "regression_count": 0, + "gate_pass": true, + "failure_taxonomy": [], + "blind_pair_count": 5 + }, + "output_execution": { + "ok": true, + "summary": { + "case_count": 5, + "variant_run_count": 10, + "command_executed_count": 10, + "model_executed_count": 10, + "recorded_fixture_count": 0, + "timing_observed_count": 10, + "token_observed_count": 10, + "token_estimated_count": 0, + "baseline_pass_rate": 23.67, + "with_skill_pass_rate": 43.67, + "delta": 20.0, + "regression_count": 0, + "failure_count": 0, + "gate_pass": true + }, + "runner": { + "mode": "command", + "command": [ + "python3", + "scripts/provider_output_eval_runner.py", + "--provider", + "deepseek", + "--model", + "deepseek-v4-flash", + "--api-format", + "chat-completions", + "--thinking", + "disabled", + "--api-key-env", + "DEEPSEEK_API_KEY" + ], + "timeout_seconds": 180.0 + }, + "failures": [] + }, + "output_blind_review": { + "summary": { + "pair_count": 5, + "answer_key_separate": true, + "with_skill_hidden_count": 5 + }, + "seed": "yao-output-eval-blind-v1", + "pair_count": 5, + "answer_key_separate": true + }, + "output_review_kit": { + "ok": true, + "summary": { + "case_count": 5, + "ready_for_adjudication_count": 0, + "pending_decision_count": 5, + "invalid_decision_count": 0, + "reviewer_metadata_present": false, + "answer_key_hidden": true, + "answer_key_path_exposed": false, + "ready_to_run_adjudication": false, + "failure_count": 0 + }, + "artifacts": { + "reviewer_kit_json": "reports/output_review_kit.json", + "reviewer_kit_markdown": "reports/output_review_kit.md", + "reviewer_kit_html": "reports/output_review_kit.html", + "blind_pack_json": "reports/output_blind_review_pack.json", + "blind_pack_markdown": "reports/output_blind_review_pack.md", + "decisions": "reports/output_review_decisions.json", + "adjudication_json": "reports/output_review_adjudication.json", + "adjudication_markdown": "reports/output_review_adjudication.md" + }, + "failures": [] + }, + "output_review_adjudication": { + "ok": true, + "summary": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "agreement_count": 0, + "disagreement_count": 0, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "pending_answer_hidden_count": 5, + "agreement_rate": null, + "needs_review": true, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "failure_count": 0, + "reviewer_checklist_count": 5, + "reviewer_checklist_pending_count": 5, + "reviewer_checklist_invalid_count": 0, + "reviewer_checklist_ready_count": 0 + }, + "reviewer": "", + "reviewed_at": "", + "failures": [] + }, + "benchmark_reproducibility": { + "ok": true, + "summary": { + "reproducibility_ready": true, + "release_lock_ready": false, + "methodology_complete": true, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "evidence_bundle_sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158", + "source_contract_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "output_case_count": 5, + "failure_disclosure_count": 3, + "command_count": 23, + "command_executed_count": 10, + "timing_observed_count": 10, + "model_executed_count": 10, + "token_observed_count": 10, + "human_review_complete": false, + "provider_evidence_complete": true, + "world_class_ready": false, + "world_class_open_gap_count": 3, + "world_class_task_count": 4, + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4, + "working_tree_dirty": true, + "changed_file_count": 37, + "source_tree_dirty": true, + "source_changed_file_count": 1, + "generated_tree_dirty": true, + "generated_changed_file_count": 36 + }, + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "missing_artifacts": [], + "limitations": [ + "The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately.", + "Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet.", + "Pending blind-review decisions are visible but do not count as human adjudication.", + "World-class readiness remains false until external and human evidence gaps close.", + "Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims." + ] + }, + "runtime_conformance": { + "target_count": 5, + "pass_count": 5, + "fail_count": 0 + }, + "runtime_permissions": { + "ok": true, + "summary": { + "target_count": 4, + "pass_count": 4, + "fail_count": 0, + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "residual_risk_count": 4, + "required_capability_count": 3, + "failure_count": 0, + "installer_enforcement_source_status": "present", + "installer_enforcement_target_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "installer_permission_capability_count": 3, + "world_class_native_evidence_ready": false, + "installer_enforcement_ready": true + }, + "expected_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "targets": [ + { + "target": "openai", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "claude", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "generic", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "vscode", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + } + ], + "failures": [] + }, + "trust_security": { + "scanned_files": 247, + "script_count": 155, + "internal_module_count": 69, + "secret_findings": 0, + "dependency_files": [ + "requirements-ci.txt" + ], + "network_script_count": 3, + "network_policy_covered_count": 3, + "network_policy_missing_count": 0, + "file_write_script_count": 74, + "permission_required_count": 3, + "permission_approved_count": 3, + "permission_missing_count": 0, + "permission_invalid_count": 0, + "permission_expired_count": 0, + "help_smoke_checked_count": 86, + "help_smoke_failed_count": 0, + "interactive_script_count": 0, + "package_hash_scope": "source-contract-without-generated-reports", + "package_hash_file_count": 247, + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + }, + "skill_atlas": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + }, + "registry_distribution": { + "ok": true, + "package": { + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "agent-skills": "pass", + "vscode": "pass", + "generic": "pass", + "agent-skills-compatible": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "package_verification": "reports/package_verification.json", + "install_simulated": true, + "install_simulation": "reports/install_simulation.json" + }, + "generated_at": "2026-06-13" + }, + "failures": [], + "warnings": [] + }, + "package_verification": { + "ok": true, + "summary": { + "target_count": 4, + "adapter_count": 4, + "archive_present": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "archive_entry_count": 580, + "nested_skill_entry_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "install_simulation": { + "ok": true, + "summary": { + "archive_present": true, + "archive_entry_count": 598, + "nested_skill_entry_count": 0, + "archive_extracted": true, + "entrypoint_loaded": true, + "manifest_loaded": true, + "interface_loaded": true, + "adapter_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "install_root_is_temp": false, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "upgrade_check": { + "ok": true, + "summary": { + "declared_bump": "minor", + "recommended_bump": "minor", + "breaking_change_count": 0, + "added_target_count": 2, + "metadata_change_count": 1, + "checksum_change_count": 2, + "failure_count": 0, + "warning_count": 0 + }, + "upgrade_diff": { + "name_changed": false, + "added_targets": [ + "agent-skills-compatible", + "vscode" + ], + "removed_targets": [], + "compatibility_changes": [ + { + "target": "agent-skills", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "agent-skills-compatible", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "vscode", + "from": "missing", + "to": "pass", + "direction": "improved" + } + ], + "metadata_changes": [ + { + "field": "description", + "from": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes.", + "to": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + } + ], + "checksum_changes": [ + { + "field": "archive_sha256", + "from": "", + "to": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + { + "field": "package_sha256", + "from": "0000000000000000000000000000000000000000000000000000000000000000", + "to": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "release_notes": [ + "Recommended version bump: minor.", + "Added targets: agent-skills-compatible, vscode.", + "Package or archive checksum changed; reviewers should verify package artifacts before release.", + "Package verification evidence: reports/package_verification.md." + ], + "failures": [], + "warnings": [] + }, + "adoption_drift": { + "ok": true, + "summary": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 100.0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "failure_types": {}, + "source_types": { + "manual": 1 + }, + "command_counts": {} + }, + "next_iteration_candidates": [], + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "failures": [] + }, + "review_waivers": { + "ok": true, + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 2, + "waiverable_open_count": 1, + "non_waivable_count": 1 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "review_studio_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "review-waivers", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "world-class-evidence" + ], + "known_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "waiverable_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "non_waivable_gate_keys": [ + "review-waivers", + "world-class-evidence" + ] + }, + "failures": [], + "warnings": [] + }, + "review_annotations": { + "ok": true, + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [] + }, + "world_class_evidence_plan": { + "ok": true, + "summary": { + "audit_decision": "continue-iteration", + "world_class_ready": false, + "audit_world_class_ready": false, + "ready_to_claim_world_class": false, + "ledger_completion_required": true, + "evidence_requirement_count": 4, + "task_count": 4, + "human_task_count": 1, + "external_task_count": 3, + "review_task_count": 0, + "decision": "collect-external-evidence" + }, + "tasks": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "status": "pass", + "category": "external", + "owner": "operator with provider credentials", + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "audit_next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "category": "human", + "owner": "human reviewer", + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "audit_next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "category": "external", + "owner": "target client or installer integrator", + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "audit_next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "audit_next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_audit": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "open_gap_count": 3 + } + }, + "world_class_evidence_ledger": { + "ok": true, + "summary": { + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "entries": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "pending", + "source_status": "pass", + "source_accepted": true, + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "pending", + "source_status": "human_required", + "source_accepted": false, + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_plan": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "task_count": 4, + "evidence_requirement_count": 4 + } + }, + "synthesis_highlights": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + }, + "metadata": { + "canonical_format": "agent-skills", + "targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "maturity_tier": "governed", + "skill_archetype": "governed", + "updated_at": "2026-03-31" + } + }, + "intent_confidence": { + "score": 100, + "band": "high", + "gate_passed": true, + "strengths": [ + "The recurring job is concrete enough to anchor the package.", + "Real input shape is explicit.", + "The hand-back output is concrete.", + "Boundary exclusions are already explicit.", + "Operational constraints are visible." + ], + "gaps": [], + "follow_up_questions": [], + "anchor_sentence": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse. Primary output: A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.. Exclusions: one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill, general code review or debugging unless the user is packaging that workflow as a skill, raw private material that was not intentionally supplied as skill evidence, platform-specific plugin creation when the request is not about an agent skill package.", + "recommended_action": "Intent is clear enough to package the first routeable version.", + "context": { + "job": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "real_inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "primary_output": "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "exclusions": [ + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill", + "raw private material that was not intentionally supplied as skill evidence", + "platform-specific plugin creation when the request is not about an agent skill package" + ], + "constraints": [ + "keep SKILL.md lean and route primarily through frontmatter description", + "put durable guidance in references, executable logic in scripts, and evidence in reports", + "default to the lightest reliable mode before adding governance weight", + "preserve portability across OpenAI, Claude, generic, and Agent Skills compatible targets", + "avoid raw prompt, output, transcript, or private content in telemetry" + ], + "standards": [ + "trigger boundaries must be tested with should-trigger and should-not-trigger cases", + "production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence", + "governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation", + "generated reports should be bilingual or reviewer-friendly when they are user-facing", + "each new asset must earn its place by reducing ambiguity, risk, or repeated work" + ], + "correction": "The 2.0 direction reframes this package from a Meta Skill factory into a Skill OS with Skill IR, compiler contracts, output eval, runtime conformance, trust, registry, Review Studio, Skill Atlas, and telemetry loops.", + "user_references": [ + "Yao Meta Skill 2.0 upgrade plan", + "Kami-style white report layout requirements for generated skill reports", + "local Waza engineering habits for review, design, diagnosis, and implementation discipline" + ] + } + }, + "intent": { + "skill_name": "yao-meta-skill", + "title": "Yao Meta Skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "focus": "portability-and-contract", + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", + "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster." + ], + "opening_styles": [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。" + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。" + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。" + } + ], + "optional_scaffold": [ + "The repeated job it should reliably handle", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse" + ], + "questions": [ + { + "question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?", + "why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape." + }, + { + "question": "When someone reaches for this skill in the real world, what materials will they actually hand to it?", + "why": "Input shape decides whether references, scripts, or templates are needed." + }, + { + "question": "What finished output should it hand back so the user can immediately keep moving?", + "why": "Outputs should drive the package structure before extra guidance is added." + }, + { + "question": "Which nearby requests should this skill politely refuse so the boundary stays clean?", + "why": "The exclusion list is the fastest route to better trigger quality." + }, + { + "question": "What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?", + "why": "Constraints decide how much structure, packaging, and review the skill actually needs." + }, + { + "question": "Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example?", + "why": "A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material." + }, + { + "question": "Which environments or clients must be able to consume this skill?", + "why": "This sets the minimum metadata and degradation strategy." + } + ], + "output": { + "capability_sentence": "Yao Meta Skill should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", + "required_capture": [ + "recurring job", + "real inputs", + "required outputs", + "exclusions", + "constraints", + "reference preferences", + "first evaluation target" + ], + "recommended_first_gate": "portability and contract" + } + }, + "reference": { + "skill_name": "yao-meta-skill", + "title": "Yao Meta Skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "scan_focus": [ + { + "label": "Evaluation pattern", + "reason": "This skill already carries eval assets, so benchmark how top examples define trigger boundaries and quality gates." + }, + { + "label": "Execution pattern", + "reason": "There is deterministic logic in scripts, so compare how strong references separate prose from executable steps." + }, + { + "label": "Portability pattern", + "reason": "The package carries neutral metadata, so scan how good references preserve semantics across targets without forking source." + }, + { + "label": "Method pattern", + "reason": "Use the core job description as the anchor for comparison: Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse." + } + ], + "external_references": [], + "user_references": [], + "local_constraints": [], + "borrow_plan": [ + "External benchmark first: let high-quality public references define the upper bound for method, structure, execution, or portability.", + "User references second: use them to understand taste, standards, and directional preferences without copying source phrasing.", + "Local fit third: use local assets only to detect naming conflicts, private dependencies, or compatibility constraints.", + "Borrow patterns, not prose: extract loops, boundaries, metadata, and operator flow without copying source-specific language.", + "Keep the package light: reject any borrowed pattern that increases context cost faster than it increases reliability." + ], + "priority_rule": "External benchmark objects set the pattern ceiling. User references refine taste and standards. Local files only calibrate fit, risk, and compatibility.", + "non_goals": [ + "Do not copy source prose or branding into the new skill.", + "Do not import gates that cost more context than they save.", + "Do not use benchmark scanning to justify scope creep.", + "Do not let local historical habits outrank stronger public benchmarks." + ] + }, + "iteration": { + "summary": { + "skill_name": "yao-meta-skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "maturity_tier": "governed", + "selection_rule": "Pick the three smallest next steps that increase reliability more than they increase context cost.", + "recommended_now": "Borrow one proven pattern on purpose", + "recommended_now_why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "defer_for_now": "Create an iteration evidence loop" + }, + "directions": [ + { + "priority": 2, + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design.", + "do_now": "Do this first.", + "wait_on": "Wait to add broader structure until this move clearly improves reliability." + }, + { + "priority": 5, + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + }, + { + "priority": 12, + "title": "Create an iteration evidence loop", + "why": "The package should show what changed and why after the first draft.", + "actions": [ + "Generate the HTML skill report and keep it aligned with the package.", + "Record reference scan choices and non-goals.", + "Capture the next iteration choice explicitly before adding more files." + ], + "unlocks": "A clearer path for the next author or reviewer.", + "do_now": "Do this after the first move lands cleanly.", + "wait_on": "Wait until the package has evidence that this extra structure is justified." + } + ] + }, + "feedback": {}, + "baseline": { + "summary": { + "target_count": 3, + "baseline_total_errors": 5, + "current_total_errors": 4, + "winner_total_errors": 4, + "winner_vs_baseline_gain": 1, + "winner_vs_current_gain": 0 + }, + "comparisons": [ + { + "label": "root", + "winner_label": "Current", + "baseline": { + "tokens": 8, + "dev_errors": 1, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "current": { + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "winner": { + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "delta": { + "current_vs_baseline": 1, + "winner_vs_baseline": 1, + "winner_vs_current": 0 + } + }, + { + "label": "team-frontend-review", + "winner_label": "Current", + "baseline": { + "tokens": 52, + "dev_errors": 3, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "current": { + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "winner": { + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "delta": { + "current_vs_baseline": 0, + "winner_vs_baseline": 0, + "winner_vs_current": 0 + } + }, + { + "label": "governed-incident-command", + "winner_label": "Current", + "baseline": { + "tokens": 93, + "dev_errors": 0, + "holdout_errors": 1, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "current": { + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "winner": { + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "blind_errors": 0, + "judge_blind_errors": 0, + "adversarial_errors": 0 + }, + "delta": { + "current_vs_baseline": 0, + "winner_vs_baseline": 0, + "winner_vs_current": 0 + } + } + ] + }, + "compare": { + "current_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "current_candidate": { + "id": "current", + "label": "Current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "strategy": "current", + "estimated_tokens": 54, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 54 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "baseline": { + "description": "Create and improve agent skills.", + "estimated_tokens": 8, + "dev": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 1, + "precision": 1.0, + "recall": 0.875, + "bucket_stats": { + "should_trigger": { + "total": 8, + "passed": 7, + "pass_rate": 0.875 + }, + "should_not_trigger": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 1, + "false_positives": 0, + "false_negatives": 1, + "pass_rate": 0.5 + }, + "complex_multi_asset": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [ + { + "bucket": "should_trigger", + "family": "iterate_existing_skill", + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "score": 0.328, + "reason": "false_negative", + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "matched_negative_concepts": [] + } + ], + "results": { + "should_trigger": [ + { + "prompt": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill", + "score": 0.387, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist", + "operations checklist" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill", + "score": 0.328, + "predicted_trigger": false, + "expected_trigger": true, + "passed": false, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.302, + "support_score": 0.04, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "trigger evals" + ], + "iterate_existing_skill": [ + "this skill", + "sharing it with the team", + "before sharing it with the team" + ], + "team_operationalize": [ + "team", + "sharing it with the team" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset", + "score": 0.844, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.042, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package", + "turn all of that into one reusable skill package" + ], + "eval_optimize": [ + "evals", + "packaging checks" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "runbook", + "process", + "prompt history" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset", + "score": 0.408, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0.06, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "production-ready" + ], + "transform_workflow": [ + "workflow", + "workflow fragments", + "scripts" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill", + "score": 0.527, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.491, + "support_score": 0.04, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "distribution" + ], + "team_operationalize": [ + "team", + "distribution" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger", + "score": 0.403, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0.04, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "team library", + "maintained" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger", + "score": 0.856, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0.04, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this workflow" + ], + "eval_optimize": [ + "validate", + "route boundary" + ], + "reuse_package": [ + "package", + "distribution", + "shared capability" + ], + "team_operationalize": [ + "shared", + "distribution" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger", + "score": 0.869, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset", + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0.1, + "lexical_support": 0.038, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill", + "skill package" + ], + "eval_optimize": [ + "validation" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "rough notes", + "transcript", + "onboarding flow", + "scripts" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Give me ideas for improving our process.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "ideas" + ] + } + } + } + }, + { + "prompt": "Review this note and tell me what it means in plain English.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "tell me what it means", + "plain english" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0.06, + "lexical_support": 0.067, + "negative_penalty": 0.66, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "improve packaging" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.083, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "translate_only": [ + "translate", + "into french" + ] + } + } + } + }, + { + "prompt": "Summarize this workflow and list the main points only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.111, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.043, + "negative_penalty": 0.68, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "explain_only": [ + "explain it", + "in simpler language" + ], + "no_build_directive": [ + "do not convert it into a reusable capability", + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only", + "score": 0.257, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.698, + "support_score": 0, + "lexical_support": 0.056, + "negative_penalty": 0.3, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "summarize_only": [ + "summary", + "recap" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "explain_only": [ + "explain it" + ], + "no_build_directive": [ + "no packaging needed" + ] + } + } + } + }, + { + "prompt": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.42, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + }, + { + "prompt": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document" + ], + "no_build_directive": [ + "not as an agent skill" + ] + } + } + } + }, + { + "prompt": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.34, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "document_only": [ + "document", + "reusable-looking document" + ], + "no_build_directive": [ + "without turning them into an agent skill" + ] + } + } + } + }, + { + "prompt": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.048, + "negative_penalty": 0.82, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document", + "template only" + ], + "future_outline": [ + "not now", + "later but not now" + ], + "no_build_directive": [ + "not now", + "later but not now" + ] + } + } + } + }, + { + "prompt": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.053, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "for later", + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + } + ] + } + }, + "holdout": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 5, + "passed": 5, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + } + }, + "family_stats": { + "complex_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset", + "score": 0.416, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset", + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0.1, + "lexical_support": 0.053, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable" + ], + "team_operationalize": [ + "internal" + ], + "transform_workflow": [ + "process", + "internal process note", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill", + "score": 0.699, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.679, + "support_score": 0.04, + "lexical_support": 0.056, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "near-neighbor", + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "package", + "distribution" + ], + "team_operationalize": [ + "distribution" + ], + "transform_workflow": [ + "existing skill draft" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill", + "score": 0.841, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill library entry" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "library entry" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger", + "score": 0.861, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0.06, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "packaging checks" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "quarterly release routine", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger", + "score": 0.719, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.698, + "support_score": 0.04, + "lexical_support": 0.08, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "turn this playbook into an agent capability package", + "agent capability" + ], + "reuse_package": [ + "package", + "team library", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "prompt history", + "playbook", + "transcript", + "release notes" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ], + "summarize_only": [ + "summary", + "only want a summary" + ] + } + } + } + }, + { + "prompt": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package", + "release" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "explain_only": [ + "explain this", + "in simpler language" + ], + "no_build_directive": [ + "do not package it" + ] + } + } + } + }, + { + "prompt": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.111, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "release" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document" + ], + "no_build_directive": [ + "not for agent execution" + ] + } + } + } + }, + { + "prompt": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.698, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into russian" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "semantic_coverage": 0.321, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.46, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ] + }, + "negative": { + "brainstorm_only": [ + "shape an idea" + ], + "future_outline": [ + "before we decide whether to build" + ] + } + } + } + }, + { + "prompt": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill", + "outline for a possible future skill" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.509, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document", + "document package", + "wiki" + ], + "no_build_directive": [ + "not an agent skill" + ] + } + } + } + }, + { + "prompt": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.698, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "workflow", + "checklist" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "no skill package", + "discussion only" + ] + } + } + } + } + ] + } + } + }, + "winner": { + "id": "current", + "label": "Current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "strategy": "current", + "estimated_tokens": 54, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 54 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + "winner_dev_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 8, + "passed": 8, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "complex_multi_asset": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill", + "score": 0.379, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist", + "operations checklist" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill", + "score": 0.333, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.286, + "support_score": 0.04, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "trigger evals" + ], + "iterate_existing_skill": [ + "this skill", + "sharing it with the team", + "before sharing it with the team" + ], + "team_operationalize": [ + "team", + "sharing it with the team" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset", + "score": 0.816, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.208, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package", + "turn all of that into one reusable skill package" + ], + "eval_optimize": [ + "evals", + "packaging checks" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "runbook", + "process", + "prompt history" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset", + "score": 0.454, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "production-ready" + ], + "transform_workflow": [ + "workflow", + "workflow fragments", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill", + "score": 0.514, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.464, + "support_score": 0.04, + "lexical_support": 0.214, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "distribution" + ], + "team_operationalize": [ + "team", + "distribution" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger", + "score": 0.39, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0.04, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "team library", + "maintained" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0.04, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this workflow" + ], + "eval_optimize": [ + "validate", + "route boundary" + ], + "reuse_package": [ + "package", + "distribution", + "shared capability" + ], + "team_operationalize": [ + "shared", + "distribution" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger", + "score": 0.871, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0.04, + "lexical_support": 0.154, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill", + "skill package" + ], + "eval_optimize": [ + "validation" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "rough notes", + "transcript", + "onboarding flow", + "scripts" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Give me ideas for improving our process.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "ideas" + ] + } + } + } + }, + { + "prompt": "Review this note and tell me what it means in plain English.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "tell me what it means", + "plain english" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.133, + "negative_penalty": 0.66, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "improve packaging" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "translate_only": [ + "translate", + "into french" + ] + } + } + } + }, + { + "prompt": "Summarize this workflow and list the main points only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.68, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "explain_only": [ + "explain it", + "in simpler language" + ], + "no_build_directive": [ + "do not convert it into a reusable capability", + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only", + "score": 0.236, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0.3, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "summarize_only": [ + "summary", + "recap" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "explain_only": [ + "explain it" + ], + "no_build_directive": [ + "no packaging needed" + ] + } + } + } + }, + { + "prompt": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.42, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + }, + { + "prompt": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document" + ], + "no_build_directive": [ + "not as an agent skill" + ] + } + } + } + }, + { + "prompt": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.321, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "document_only": [ + "document", + "reusable-looking document" + ], + "no_build_directive": [ + "without turning them into an agent skill" + ] + } + } + } + }, + { + "prompt": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.048, + "negative_penalty": 0.82, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document", + "template only" + ], + "future_outline": [ + "not now", + "later but not now" + ], + "no_build_directive": [ + "not now", + "later but not now" + ] + } + } + } + }, + { + "prompt": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "for later", + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + } + ] + } + }, + "winner_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 5, + "passed": 5, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + } + }, + "family_stats": { + "complex_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset", + "score": 0.465, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0.04, + "lexical_support": 0.211, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable" + ], + "team_operationalize": [ + "internal" + ], + "transform_workflow": [ + "process", + "internal process note", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill", + "score": 0.684, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.643, + "support_score": 0.04, + "lexical_support": 0.278, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "near-neighbor", + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "package", + "distribution" + ], + "team_operationalize": [ + "distribution" + ], + "transform_workflow": [ + "existing skill draft" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill", + "score": 0.805, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill library entry" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "library entry" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger", + "score": 0.874, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "packaging checks" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "quarterly release routine", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger", + "score": 0.691, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.661, + "support_score": 0.04, + "lexical_support": 0.16, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "turn this playbook into an agent capability package", + "agent capability" + ], + "reuse_package": [ + "package", + "team library", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "prompt history", + "playbook", + "transcript", + "release notes" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ], + "summarize_only": [ + "summary", + "only want a summary" + ] + } + } + } + }, + { + "prompt": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package", + "release" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "explain_only": [ + "explain this", + "in simpler language" + ], + "no_build_directive": [ + "do not package it" + ] + } + } + } + }, + { + "prompt": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.167, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "release" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document" + ], + "no_build_directive": [ + "not for agent execution" + ] + } + } + } + }, + { + "prompt": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.188, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into russian" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "semantic_coverage": 0.304, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.46, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ] + }, + "negative": { + "brainstorm_only": [ + "shape an idea" + ], + "future_outline": [ + "before we decide whether to build" + ] + } + } + } + }, + { + "prompt": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.214, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill", + "outline for a possible future skill" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.308, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document", + "document package", + "wiki" + ], + "no_build_directive": [ + "not an agent skill" + ] + } + } + } + }, + { + "prompt": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.375, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "workflow", + "checklist" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "no skill package", + "discussion only" + ] + } + } + } + } + ] + } + }, + "current_dev_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 8, + "passed": 8, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 7, + "passed": 7, + "pass_rate": 1.0 + } + }, + "family_stats": { + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "complex_multi_asset": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "brainstorm_only": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_not_package": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "partial_scaffold_not_full_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Convert this operations checklist into a reusable skill.", + "family": "workflow_to_skill", + "score": 0.379, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.125, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "checklist", + "operations checklist" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Add trigger evals to this skill before sharing it with the team.", + "family": "iterate_existing_skill", + "score": 0.333, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.286, + "support_score": 0.04, + "lexical_support": 0.25, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "trigger evals" + ], + "iterate_existing_skill": [ + "this skill", + "sharing it with the team", + "before sharing it with the team" + ], + "team_operationalize": [ + "team", + "sharing it with the team" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "We have a messy release runbook, export process, and a prompt history; turn all of that into one reusable skill package with evals and packaging checks.", + "family": "complex_multi_asset", + "score": 0.816, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.208, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package", + "turn all of that into one reusable skill package" + ], + "eval_optimize": [ + "evals", + "packaging checks" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "runbook", + "process", + "prompt history" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn these workflow fragments, transcripts, and prompts into one production-ready skill with scripts and references.", + "family": "complex_multi_asset", + "score": 0.454, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "production-ready" + ], + "transform_workflow": [ + "workflow", + "workflow fragments", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Tighten the trigger boundary on this existing skill and prepare it for team distribution.", + "family": "iterate_existing_skill", + "score": 0.514, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.464, + "support_score": 0.04, + "lexical_support": 0.214, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "distribution" + ], + "team_operationalize": [ + "team", + "distribution" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn the SOP, transcript, and cleanup notes into a maintained skill for the team library.", + "family": "paraphrase_trigger", + "score": 0.39, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0.04, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "team library", + "maintained" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Codify this workflow into a shared capability package and validate the route boundary before distribution.", + "family": "paraphrase_trigger", + "score": 0.815, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0.04, + "lexical_support": 0.067, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this workflow" + ], + "eval_optimize": [ + "validate", + "route boundary" + ], + "reuse_package": [ + "package", + "distribution", + "shared capability" + ], + "team_operationalize": [ + "shared", + "distribution" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "After the long handoff notes below, build a skill package for team reuse. The materials include a transcript, rough notes, and an onboarding flow, and the result should include references, scripts, and validation.", + "family": "long_context_trigger", + "score": 0.871, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0.04, + "lexical_support": 0.154, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill", + "skill package" + ], + "eval_optimize": [ + "validation" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reuse", + "package", + "team reuse" + ], + "team_operationalize": [ + "team", + "team reuse" + ], + "transform_workflow": [ + "rough notes", + "transcript", + "onboarding flow", + "scripts" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Give me ideas for improving our process.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only" + ], + "exclusive_negative_concepts": [ + "brainstorm_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.24, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "process" + ] + }, + "negative": { + "brainstorm_only": [ + "ideas" + ] + } + } + } + }, + { + "prompt": "Review this note and tell me what it means in plain English.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "tell me what it means", + "plain english" + ] + } + } + } + }, + { + "prompt": "Help me brainstorm several ways to improve packaging, but do not generate any skill files.", + "family": "brainstorm_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "no_build_directive" + ], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.133, + "negative_penalty": 0.66, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm", + "improve packaging" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Translate these notes into French and keep the structure exactly as-is.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "translate_only" + ], + "exclusive_negative_concepts": [ + "translate_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.35, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "translate_only": [ + "translate", + "into french" + ] + } + } + } + }, + { + "prompt": "Summarize this workflow and list the main points only.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.3, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "summarize_only": [ + "summarize" + ] + } + } + } + }, + { + "prompt": "This is a long incident write-up. Keep it as documentation only, explain it in simpler language, and do not convert it into a reusable capability.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.0, + "negative_penalty": 0.68, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "explain_only": [ + "explain it", + "in simpler language" + ], + "no_build_directive": [ + "do not convert it into a reusable capability", + "keep it as documentation only" + ] + } + } + } + }, + { + "prompt": "I pasted a very long process note. For now I only need a recap and a short summary, not a skill package.", + "family": "long_context_summary_only", + "score": 0.236, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "summarize_only" + ], + "exclusive_negative_concepts": [ + "summarize_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.222, + "negative_penalty": 0.3, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "summarize_only": [ + "summary", + "recap" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Review this process note and explain it, no packaging needed.", + "family": "explain_not_package", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "explain_only": [ + "explain it" + ], + "no_build_directive": [ + "no packaging needed" + ] + } + } + } + }, + { + "prompt": "Turn this into a checklist and template, but stop short of making a full skill.", + "family": "partial_scaffold_not_full_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0.42, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "no_build_directive": [ + "stop short of making a full skill" + ] + } + } + } + }, + { + "prompt": "Package this explanation as a document, not as an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.3, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document" + ], + "no_build_directive": [ + "not as an agent skill" + ] + } + } + } + }, + { + "prompt": "Make a future-ready outline for this skill idea, but do not build the package.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "iterate_existing_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.321, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "iterate_existing_skill": [ + "this skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Polish these notes into a reusable-looking document without turning them into an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "reusable" + ] + }, + "negative": { + "document_only": [ + "document", + "reusable-looking document" + ], + "no_build_directive": [ + "without turning them into an agent skill" + ] + } + } + } + }, + { + "prompt": "Turn the workflow below into a wiki-ready reference document and template only. We may build a skill later, but not now.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.048, + "negative_penalty": 0.82, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document", + "template only" + ], + "future_outline": [ + "not now", + "later but not now" + ], + "no_build_directive": [ + "not now", + "later but not now" + ] + } + } + } + }, + { + "prompt": "Shape this idea for later and produce a future-ready outline, but do not build the skill package yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0.64, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "future_outline": [ + "for later", + "future-ready outline" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + } + ] + } + }, + "current_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 5, + "passed": 5, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 4, + "passed": 4, + "pass_rate": 1.0 + } + }, + "family_stats": { + "complex_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "iterate_existing_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "workflow_to_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "paraphrase_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_trigger": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "summary_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "translate_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "future_outline_vs_build": { + "total": 2, + "passed": 2, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "document_export_vs_agent_skill": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "long_context_near_neighbor": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Build a reusable skill from this long internal process note, and make sure it has references, scripts, and a trigger description.", + "family": "complex_multi_asset", + "score": 0.465, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.411, + "support_score": 0.04, + "lexical_support": 0.211, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable" + ], + "team_operationalize": [ + "internal" + ], + "transform_workflow": [ + "process", + "internal process note", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take this existing skill draft, tighten the trigger boundary, add near-neighbor evals, and package it for distribution.", + "family": "iterate_existing_skill", + "score": 0.684, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.643, + "support_score": 0.04, + "lexical_support": 0.278, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "eval_optimize": [ + "evals", + "near-neighbor", + "trigger boundary" + ], + "iterate_existing_skill": [ + "existing skill" + ], + "reuse_package": [ + "package", + "distribution" + ], + "team_operationalize": [ + "distribution" + ], + "transform_workflow": [ + "existing skill draft" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Turn this recurring onboarding process into a reusable skill library entry with validation notes.", + "family": "workflow_to_skill", + "score": 0.805, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill library entry" + ], + "eval_optimize": [ + "validation" + ], + "reuse_package": [ + "reusable", + "library entry" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this quarterly release routine into a reusable skill package with scripts, references, and packaging checks.", + "family": "paraphrase_trigger", + "score": 0.874, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.857, + "support_score": 0, + "lexical_support": 0.312, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "packaging checks" + ], + "multi_asset": [ + "references", + "scripts" + ], + "reuse_package": [ + "reusable", + "package", + "packaging", + "release" + ], + "transform_workflow": [ + "quarterly release routine", + "scripts" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Below is a noisy transcript plus release notes and prompt history. Ignore the chatter and turn this playbook into an agent capability package for the team library.", + "family": "long_context_trigger", + "score": 0.691, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "team_operationalize" + ], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.661, + "support_score": 0.04, + "lexical_support": 0.16, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "turn this playbook into an agent capability package", + "agent capability" + ], + "reuse_package": [ + "package", + "team library", + "release" + ], + "team_operationalize": [ + "team" + ], + "transform_workflow": [ + "prompt history", + "playbook", + "transcript", + "release notes" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "I pasted a long process description below, but I only want a summary, not a reusable skill.", + "family": "summary_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0.72, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "no_build_directive": [ + "not a reusable skill" + ], + "summarize_only": [ + "summary", + "only want a summary" + ] + } + } + } + }, + { + "prompt": "Explain this release checklist in simpler language; do not package it.", + "family": "explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "explain_only", + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.68, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "package", + "release" + ], + "transform_workflow": [ + "checklist" + ] + }, + "negative": { + "explain_only": [ + "explain this", + "in simpler language" + ], + "no_build_directive": [ + "do not package it" + ] + } + } + } + }, + { + "prompt": "Keep this release workflow as documentation only for the wiki and produce a reference document, not for agent execution.", + "family": "long_context_document_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.167, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "release" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "document", + "wiki", + "reference document" + ], + "no_build_directive": [ + "not for agent execution" + ] + } + } + } + }, + { + "prompt": "Translate this long runbook into Russian and keep it as documentation only with no skill package.", + "family": "translate_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "translate_only" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.188, + "negative_penalty": 0.77, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "no_build_directive": [ + "no skill package", + "keep it as documentation only" + ], + "translate_only": [ + "translate", + "into russian" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Help me shape an idea before we decide whether to build a skill.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline" + ], + "semantic_coverage": 0.304, + "support_score": 0, + "lexical_support": 0.077, + "negative_penalty": 0.46, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "build_skill": [ + "build a skill" + ] + }, + "negative": { + "brainstorm_only": [ + "shape an idea" + ], + "future_outline": [ + "before we decide whether to build" + ] + } + } + } + }, + { + "prompt": "Create an outline for a possible future skill, but do not build the skill yet.", + "family": "future_outline_vs_build", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.214, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill", + "outline for a possible future skill" + ], + "no_build_directive": [ + "do not build" + ] + } + } + } + }, + { + "prompt": "Turn this into a document package for the wiki, not an agent skill.", + "family": "document_export_vs_agent_skill", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.482, + "support_score": 0, + "lexical_support": 0.308, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "build_skill": [ + "agent skill" + ], + "reuse_package": [ + "package" + ] + }, + "negative": { + "document_only": [ + "document", + "document package", + "wiki" + ], + "no_build_directive": [ + "not an agent skill" + ] + } + } + } + }, + { + "prompt": "From the long workflow below, create a template only and a checklist for discussion only. No skill package.", + "family": "long_context_near_neighbor", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.661, + "support_score": 0, + "lexical_support": 0.375, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "workflow", + "checklist" + ] + }, + "negative": { + "document_only": [ + "template only" + ], + "no_build_directive": [ + "no skill package", + "discussion only" + ] + } + } + } + } + ] + } + }, + "winner_blind_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "score": 0.38, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "score": 0.374, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "score": 0.084, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.136, + "negative_penalty": 0.18, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + } + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.054, + "support_score": 0, + "lexical_support": 0.154, + "negative_penalty": 0.22, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + } + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "score": 0.013, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.22, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + } + } + ] + } + }, + "current_blind_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "score": 0.38, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.143, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "score": 0.374, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "score": 0.084, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.232, + "support_score": 0, + "lexical_support": 0.136, + "negative_penalty": 0.18, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + } + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.054, + "support_score": 0, + "lexical_support": 0.154, + "negative_penalty": 0.22, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + } + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "score": 0.013, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.357, + "support_score": 0, + "lexical_support": 0.182, + "negative_penalty": 0.22, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + } + } + ] + } + }, + "baseline_blind_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "score": 0.393, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.071, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + }, + "boundary_case": true + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "score": 0.392, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 0, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + }, + "boundary_case": true + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "score": 0.019, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "document_only" + ], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.189, + "support_score": 0.06, + "lexical_support": 0.136, + "negative_penalty": 0.18, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + } + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ], + "exclusive_negative_concepts": [ + "explain_only" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.2, + "negative_penalty": 0.26, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.0, + "support_score": 0.06, + "lexical_support": 0.0, + "negative_penalty": 0.22, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + } + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "score": 0.024, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline" + ], + "exclusive_negative_concepts": [ + "future_outline" + ], + "semantic_coverage": 0.377, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.22, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + } + } + ] + } + }, + "winner_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 0, + "margin": 3, + "confidence": 0.74, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "multi_asset" + ], + "strong_positive_concepts": [ + "transform_workflow" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [ + "document_only" + ], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 1, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "prompts" + ], + "artifact_hits": [ + "prompts" + ], + "capability_overlap": 2, + "exclusive_negative_concepts": [ + "explain_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain", + "plain english" + ], + "positive_vote": 2, + "negative_vote": 3, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "multi_asset" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 2, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 2, + "margin": 1, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "current_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 0, + "margin": 3, + "confidence": 0.74, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "multi_asset" + ], + "strong_positive_concepts": [ + "transform_workflow" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [ + "document_only" + ], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 1, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "prompts" + ], + "artifact_hits": [ + "prompts" + ], + "capability_overlap": 2, + "exclusive_negative_concepts": [ + "explain_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain", + "plain english" + ], + "positive_vote": 2, + "negative_vote": 3, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "multi_asset" + ], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 2, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 2, + "margin": 1, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "baseline_judge_blind_holdout_report": { + "judge": "rubric-blind-v1", + "judge_explanation": "The blind judge uses a rubric rather than the main threshold scorer. It looks for trigger-action evidence, focused capability evidence, and input or artifact evidence, then blocks on explicit exclusion and exclusive negative signals. This acts as an independent second opinion for blind-holdout prompts.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "blind_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_multi_asset": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_document_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_explain_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_future_outline": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "blind_brainstorm_only": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Operationalize this support escalation routine as a maintained skill and keep trigger eval coverage.", + "family": "blind_operationalize", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 0, + "margin": 3, + "confidence": 0.74, + "concept_evidence": { + "positive": { + "reuse_package": [ + "maintained", + "operationalize" + ], + "transform_workflow": [ + "support escalation routine" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Take these SOP scraps, prompt notes, and transcript excerpts and formalize them into a reusable capability package.", + "family": "blind_multi_asset", + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 2, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 4, + "negative_vote": 0, + "margin": 4, + "confidence": 0.82, + "concept_evidence": { + "positive": { + "reuse_package": [ + "reusable", + "package" + ], + "transform_workflow": [ + "sop", + "transcript" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Turn this workflow note into a wiki reference only, do not create any skill files, and keep it out of agent execution.", + "family": "blind_document_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow" + ], + "strong_positive_concepts": [ + "transform_workflow" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 3, + "exclusive_negative_concepts": [], + "nonexclusive_negative_concepts": [ + "document_only" + ], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 2, + "negative_vote": 1, + "margin": 1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": { + "document_only": [ + "wiki" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Explain the differences between prompts and skills in plain English.", + "family": "blind_explain_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "prompts" + ], + "artifact_hits": [ + "prompts" + ], + "capability_overlap": 2, + "exclusive_negative_concepts": [ + "explain_only" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [ + "explain", + "plain english" + ], + "positive_vote": 2, + "negative_vote": 3, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": {}, + "negative": { + "explain_only": [ + "plain english" + ] + } + } + }, + "boundary_case": true + } + ], + "near_neighbor": [ + { + "prompt": "Sketch a future skill outline from these notes, but stop before generating files.", + "family": "blind_future_outline", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [], + "strong_positive_concepts": [], + "trigger_action_hits": [], + "input_hits": [ + "notes" + ], + "artifact_hits": [], + "capability_overlap": 0, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 1, + "negative_vote": 2, + "margin": -1, + "confidence": 0.58, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ] + }, + "negative": { + "future_outline": [ + "future skill" + ] + } + } + }, + "boundary_case": true + }, + { + "prompt": "Review this process and suggest improvements for later; no packaging yet.", + "family": "blind_brainstorm_only", + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "judge_detail": { + "mode": "judge-rubric", + "focused_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "strong_positive_concepts": [ + "transform_workflow", + "reuse_package" + ], + "trigger_action_hits": [], + "input_hits": [], + "artifact_hits": [], + "capability_overlap": 1, + "exclusive_negative_concepts": [ + "future_outline" + ], + "nonexclusive_negative_concepts": [], + "exclusion_hits": [], + "generic_exclusion_hits": [], + "positive_vote": 3, + "negative_vote": 2, + "margin": 1, + "confidence": 0.66, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ], + "transform_workflow": [ + "process" + ] + }, + "negative": { + "future_outline": [ + "for later" + ] + } + } + }, + "boundary_case": true + } + ] + }, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "winner_adversarial_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_multi_asset_boundary": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_document_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_summary_translation_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_outline_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_brainstorm_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the noisy meeting chatter and operationalize this support escalation workflow into a maintained skill package with trigger hardening.", + "family": "adversarial_noisy_operationalize", + "score": 0.812, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "hardening" + ], + "reuse_package": [ + "package", + "maintained", + "operationalize" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this routine into a reusable capability from docs, transcripts, and prompt history, then stress test the route boundary.", + "family": "adversarial_multi_asset_boundary", + "score": 0.808, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.105, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this routine" + ], + "eval_optimize": [ + "stress test", + "route boundary" + ], + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "prompt history" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Create a wiki package from this runbook and do not generate any skill files.", + "family": "adversarial_document_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "document_only": [ + "wiki" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Summarize this SOP, translate it into Japanese for the knowledge base, and keep it as documentation only.", + "family": "adversarial_summary_translation_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive", + "summarize_only", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only", + "translate_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 1.25, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "sop" + ] + }, + "negative": { + "document_only": [ + "knowledge base" + ], + "no_build_directive": [ + "keep it as documentation only" + ], + "summarize_only": [ + "summarize" + ], + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Outline a possible future skill from these notes, but stop short of making a full skill and keep it as a plain note.", + "family": "adversarial_future_outline_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill" + ], + "no_build_directive": [ + "stop short of making a full skill", + "plain note", + "keep it as a plain note" + ] + } + } + } + }, + { + "prompt": "Brainstorm packaging improvements for later and keep this as discussion only.", + "family": "adversarial_brainstorm_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.88, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ], + "future_outline": [ + "for later" + ], + "no_build_directive": [ + "discussion only" + ] + } + } + } + } + ] + } + }, + "current_adversarial_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_multi_asset_boundary": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_document_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_summary_translation_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_outline_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_brainstorm_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the noisy meeting chatter and operationalize this support escalation workflow into a maintained skill package with trigger hardening.", + "family": "adversarial_noisy_operationalize", + "score": 0.812, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.158, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "hardening" + ], + "reuse_package": [ + "package", + "maintained", + "operationalize" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this routine into a reusable capability from docs, transcripts, and prompt history, then stress test the route boundary.", + "family": "adversarial_multi_asset_boundary", + "score": 0.808, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.804, + "support_score": 0, + "lexical_support": 0.105, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this routine" + ], + "eval_optimize": [ + "stress test", + "route boundary" + ], + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "prompt history" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Create a wiki package from this runbook and do not generate any skill files.", + "family": "adversarial_document_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.411, + "support_score": 0, + "lexical_support": 0.286, + "negative_penalty": 0.6, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "document_only": [ + "wiki" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Summarize this SOP, translate it into Japanese for the knowledge base, and keep it as documentation only.", + "family": "adversarial_summary_translation_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive", + "summarize_only", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only", + "translate_only" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 1.25, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "sop" + ] + }, + "negative": { + "document_only": [ + "knowledge base" + ], + "no_build_directive": [ + "keep it as documentation only" + ], + "summarize_only": [ + "summarize" + ], + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Outline a possible future skill from these notes, but stop short of making a full skill and keep it as a plain note.", + "family": "adversarial_future_outline_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.1, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill" + ], + "no_build_directive": [ + "stop short of making a full skill", + "plain note", + "keep it as a plain note" + ] + } + } + } + }, + { + "prompt": "Brainstorm packaging improvements for later and keep this as discussion only.", + "family": "adversarial_brainstorm_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill", + "multi_asset" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.179, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.88, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ], + "future_outline": [ + "for later" + ], + "no_build_directive": [ + "discussion only" + ] + } + } + } + } + ] + } + }, + "baseline_adversarial_holdout_report": { + "threshold": 0.33, + "threshold_explanation": "Prompts at or above the threshold are treated as trigger matches. Scores are driven primarily by semantic intent coverage: packaging intent, workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. Explicit exclusions such as summary-only, translation-only, one-off, document-only, or do-not-build directives apply direct penalties and can override otherwise similar wording.", + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "bucket_stats": { + "should_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "should_not_trigger": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + }, + "near_neighbor": { + "total": 2, + "passed": 2, + "pass_rate": 1.0 + } + }, + "family_stats": { + "adversarial_noisy_operationalize": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_multi_asset_boundary": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_document_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_summary_translation_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_future_outline_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + }, + "adversarial_brainstorm_collision": { + "total": 1, + "passed": 1, + "false_positives": 0, + "false_negatives": 0, + "pass_rate": 1.0 + } + }, + "misfires": [], + "results": { + "should_trigger": [ + { + "prompt": "Ignore the noisy meeting chatter and operationalize this support escalation workflow into a maintained skill package with trigger hardening.", + "family": "adversarial_noisy_operationalize", + "score": 0.845, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.053, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "skill package" + ], + "eval_optimize": [ + "hardening" + ], + "reuse_package": [ + "package", + "maintained", + "operationalize" + ], + "transform_workflow": [ + "workflow" + ] + }, + "negative": {} + } + } + }, + { + "prompt": "Codify this routine into a reusable capability from docs, transcripts, and prompt history, then stress test the route boundary.", + "family": "adversarial_multi_asset_boundary", + "score": 0.845, + "predicted_trigger": true, + "expected_trigger": true, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "build_skill", + "eval_optimize", + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [], + "exclusive_negative_concepts": [], + "semantic_coverage": 0.849, + "support_score": 0, + "lexical_support": 0.053, + "negative_penalty": 0, + "coverage_boost": 0.06, + "concept_evidence": { + "positive": { + "build_skill": [ + "codify this routine" + ], + "eval_optimize": [ + "stress test", + "route boundary" + ], + "reuse_package": [ + "reusable" + ], + "transform_workflow": [ + "prompt history" + ] + }, + "negative": {} + } + } + } + ], + "should_not_trigger": [ + { + "prompt": "Create a wiki package from this runbook and do not generate any skill files.", + "family": "adversarial_document_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "extra_positive_concepts": [ + "multi_asset" + ], + "matched_negative_concepts": [ + "document_only", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "no_build_directive" + ], + "semantic_coverage": 0.377, + "support_score": 0.06, + "lexical_support": 0.143, + "negative_penalty": 0.6, + "coverage_boost": 0.04, + "concept_evidence": { + "positive": { + "multi_asset": [ + "files" + ], + "reuse_package": [ + "package" + ], + "transform_workflow": [ + "runbook" + ] + }, + "negative": { + "document_only": [ + "wiki" + ], + "no_build_directive": [ + "do not generate any skill files" + ] + } + } + } + }, + { + "prompt": "Summarize this SOP, translate it into Japanese for the knowledge base, and keep it as documentation only.", + "family": "adversarial_summary_translation_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "transform_workflow" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "document_only", + "no_build_directive", + "summarize_only", + "translate_only" + ], + "exclusive_negative_concepts": [ + "no_build_directive", + "summarize_only", + "translate_only" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.062, + "negative_penalty": 1.25, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "transform_workflow": [ + "sop" + ] + }, + "negative": { + "document_only": [ + "knowledge base" + ], + "no_build_directive": [ + "keep it as documentation only" + ], + "summarize_only": [ + "summarize" + ], + "translate_only": [ + "translate", + "into japanese" + ] + } + } + } + } + ], + "near_neighbor": [ + { + "prompt": "Outline a possible future skill from these notes, but stop short of making a full skill and keep it as a plain note.", + "family": "adversarial_future_outline_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.0, + "support_score": 0, + "lexical_support": 0.05, + "negative_penalty": 0.64, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": {}, + "negative": { + "future_outline": [ + "future skill", + "possible future skill" + ], + "no_build_directive": [ + "stop short of making a full skill", + "plain note", + "keep it as a plain note" + ] + } + } + } + }, + { + "prompt": "Brainstorm packaging improvements for later and keep this as discussion only.", + "family": "adversarial_brainstorm_collision", + "score": 0.0, + "predicted_trigger": false, + "expected_trigger": false, + "passed": true, + "score_detail": { + "mode": "semantic-intent", + "desired_positive_concepts": [ + "build_skill", + "transform_workflow", + "reuse_package", + "eval_optimize", + "iterate_existing_skill" + ], + "matched_desired_concepts": [ + "reuse_package" + ], + "extra_positive_concepts": [], + "matched_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "exclusive_negative_concepts": [ + "brainstorm_only", + "future_outline", + "no_build_directive" + ], + "semantic_coverage": 0.189, + "support_score": 0, + "lexical_support": 0.091, + "negative_penalty": 0.88, + "coverage_boost": 0.0, + "concept_evidence": { + "positive": { + "reuse_package": [ + "packaging" + ] + }, + "negative": { + "brainstorm_only": [ + "brainstorm" + ], + "future_outline": [ + "for later" + ], + "no_build_directive": [ + "discussion only" + ] + } + } + } + } + ] + } + }, + "candidates": [ + { + "id": "current", + "label": "Current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "strategy": "current", + "estimated_tokens": 54, + "dev": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "selection_key": [ + 0, + 0, + -1.0, + -1.0, + -1.0, + -1.0, + 54 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "minimal", + "label": "Minimal", + "description": "Create, refactor, evaluate, and package agent skills. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "strategy": "minimal_template", + "estimated_tokens": 41, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 41 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "guardrail", + "label": "Guardrail", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "strategy": "guardrail_template", + "estimated_tokens": 56, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 56 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "balanced", + "label": "Balanced", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "strategy": "balanced_template", + "estimated_tokens": 60, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 60 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "artifact_aware", + "label": "Artifact Aware", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Trigger when requests mention workflows, prompts, transcripts, or docs and the job is to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "strategy": "artifact_template", + "estimated_tokens": 77, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 77 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + }, + { + "id": "boundary", + "label": "Boundary", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "strategy": "boundary_template", + "estimated_tokens": 83, + "dev": { + "false_positives": 1, + "false_negatives": 1, + "precision": 0.875, + "recall": 0.875, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 0.857 + }, + "selection_key": [ + 1, + 1, + -1.0, + -0.857, + -0.875, + -0.875, + 83 + ], + "holdout": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + } + } + ], + "selection_logic": { + "priority": [ + "fewest false positives", + "fewest false negatives", + "highest near-neighbor pass rate", + "highest negative pass rate", + "highest precision", + "highest recall", + "shortest description" + ] + }, + "comparison": { + "winner_vs_current_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_dev": { + "baseline_false_positives": 0, + "baseline_false_negatives": 1, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": -1, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 0.875, + "improved_recall": 1.0 + }, + "winner_vs_baseline_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_judge_blind_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_current_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + }, + "winner_vs_baseline_adversarial_holdout": { + "baseline_false_positives": 0, + "baseline_false_negatives": 0, + "improved_false_positives": 0, + "improved_false_negatives": 0, + "false_positive_delta": 0, + "false_negative_delta": 0, + "baseline_precision": 1.0, + "improved_precision": 1.0, + "baseline_recall": 1.0, + "improved_recall": 1.0 + } + }, + "acceptance_gates": { + "selection_basis": "dev only", + "holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "current_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "baseline_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.707, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.416, + "max_non_trigger_score": 0.0, + "score_gap": 0.416, + "threshold_margin": 0.086, + "positive_threshold_buffer": 0.086, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "winner_family_health": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "current_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "baseline_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.393, + "mean_non_trigger_score": 0.011, + "mean_near_neighbor_score": 0.012, + "min_positive_score": 0.392, + "max_non_trigger_score": 0.024, + "score_gap": 0.368, + "threshold_margin": 0.062, + "positive_threshold_buffer": 0.062, + "negative_threshold_buffer": 0.306, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "judge_blind_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0, + "judge_summary": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "winner_calibration": null, + "current_calibration": null, + "baseline_calibration": null, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "adversarial_holdout_non_regression": { + "winner": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "current": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "baseline": { + "false_positives": 0, + "false_negatives": 0, + "precision": 1.0, + "recall": 1.0, + "near_neighbor_pass_rate": 1.0, + "should_not_trigger_pass_rate": 1.0 + }, + "winner_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "current_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "baseline_calibration": { + "threshold": 0.33, + "mean_positive_score": 0.845, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.845, + "max_non_trigger_score": 0.0, + "score_gap": 0.845, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.515, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "winner_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + }, + "current_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + }, + "baseline_family_health": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + } + }, + "summary": { + "winner_label": "Current", + "winner_tokens": 54, + "current_tokens": 54, + "winner_dev_total_errors": 0, + "current_dev_total_errors": 0, + "winner_holdout_total_errors": 0, + "current_holdout_total_errors": 0, + "winner_blind_holdout_total_errors": 0, + "current_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_total_errors": 0, + "current_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_total_errors": 0, + "current_adversarial_holdout_total_errors": 0, + "winner_judge_blind_agreement_rate": 1.0, + "winner_adversarial_risk_band": "healthy", + "winner_adversarial_score_gap": 0.808, + "candidate_count": 6, + "baseline_tokens": 8, + "baseline_dev_total_errors": 1, + "baseline_holdout_total_errors": 0, + "baseline_blind_holdout_total_errors": 0, + "baseline_judge_blind_holdout_total_errors": 0, + "baseline_adversarial_holdout_total_errors": 0 + } + }, + "promotion": { + "ok": true, + "summary": { + "promote": 0, + "keep_current": 3, + "blocked": 0 + }, + "targets": [ + { + "name": "yao-meta-skill", + "winner_label": "Current", + "winner_description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "winner_tokens": 54, + "current_tokens": 54, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.33, + "mean_positive_score": 0.704, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.465, + "max_non_trigger_score": 0.0, + "score_gap": 0.465, + "threshold_margin": 0.135, + "positive_threshold_buffer": 0.135, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "blind_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.377, + "mean_non_trigger_score": 0.024, + "mean_near_neighbor_score": 0.006, + "min_positive_score": 0.374, + "max_non_trigger_score": 0.084, + "score_gap": 0.29, + "threshold_margin": 0.044, + "positive_threshold_buffer": 0.044, + "negative_threshold_buffer": 0.246, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "watch" + }, + "adversarial_holdout": { + "threshold": 0.33, + "mean_positive_score": 0.81, + "mean_non_trigger_score": 0.0, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.808, + "max_non_trigger_score": 0.0, + "score_gap": 0.808, + "threshold_margin": 0.33, + "positive_threshold_buffer": 0.478, + "negative_threshold_buffer": 0.33, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.66, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 12, + "clean_family_count": 12, + "failing_families": [], + "weakest_family": { + "family": "workflow_to_skill", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_operationalize", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_summary_translation_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.644, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "yao-meta-skill", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Create and improve agent skills.", + "tokens": 8, + "dev_errors": 1, + "holdout_errors": 0 + }, + { + "name": "yao-meta-skill", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Minimal", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 41, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 56, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 60, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Trigger when requests mention workflows, prompts, transcripts, or docs and the job is to create a skill, turn a repeated process into a reusable skill, or improve an existing skill.", + "tokens": 77, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + }, + { + "name": "yao-meta-skill", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, notes, 流程, 提示词, 对话记录, 文档, or 笔记. Use when asked to create a skill, turn a repeated process into a reusable skill, or improve an existing skill. Do not use for summary-only requests, translation-only requests, or plain documentation tasks.", + "tokens": 83, + "dev_errors": 2, + "holdout_errors": 0, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "team-frontend-review", + "winner_label": "Current", + "winner_description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "winner_tokens": 50, + "current_tokens": 50, + "winner_holdout_fp": 0, + "winner_holdout_fn": 0, + "current_holdout_fp": 0, + "current_holdout_fn": 0, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 0, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.089, + "mean_near_neighbor_score": 0.045, + "min_positive_score": 0.496, + "max_non_trigger_score": 0.211, + "score_gap": 0.285, + "threshold_margin": 0.016, + "positive_threshold_buffer": 0.016, + "negative_threshold_buffer": 0.269, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "tight" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.685, + "mean_non_trigger_score": 0.177, + "mean_near_neighbor_score": 0.226, + "min_positive_score": 0.504, + "max_non_trigger_score": 0.416, + "score_gap": 0.088, + "threshold_margin": 0.024, + "positive_threshold_buffer": 0.024, + "negative_threshold_buffer": 0.064, + "boundary_case_count": 2, + "boundary_case_rate": 0.333, + "risk_band": "tight" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.71, + "mean_non_trigger_score": 0.009, + "mean_near_neighbor_score": 0.018, + "min_positive_score": 0.544, + "max_non_trigger_score": 0.035, + "score_gap": 0.509, + "threshold_margin": 0.204, + "positive_threshold_buffer": 0.204, + "negative_threshold_buffer": 0.305, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.703, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "security_and_a11y", + "pass_rate": 1.0, + "errors": 0 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_premerge", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_quality_gate_review", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.805, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "team-frontend-review", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "tokens": 52, + "dev_errors": 3, + "holdout_errors": 0 + }, + { + "name": "team-frontend-review", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 50, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 62, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 64, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Trigger when requests mention React components, forms, frontend diffs, or UI code and the job is to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "tokens": 84, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "team-frontend-review", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions from React components, forms, frontend diffs, or UI code. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering. Do not use for building a new page from scratch, backend-only review, or explaining frontend concepts.", + "tokens": 90, + "dev_errors": 3, + "holdout_errors": 0, + "reason_tags": [ + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + }, + { + "name": "governed-incident-command", + "winner_label": "Current", + "winner_description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "winner_tokens": 37, + "current_tokens": 37, + "winner_holdout_fp": 0, + "winner_holdout_fn": 1, + "current_holdout_fp": 0, + "current_holdout_fn": 1, + "baseline_holdout_fp": 0, + "baseline_holdout_fn": 1, + "winner_blind_holdout_fp": 0, + "winner_blind_holdout_fn": 0, + "current_blind_holdout_fp": 0, + "current_blind_holdout_fn": 0, + "baseline_blind_holdout_fp": 0, + "baseline_blind_holdout_fn": 0, + "winner_blind_holdout_total_errors": 0, + "winner_judge_blind_holdout_fp": 0, + "winner_judge_blind_holdout_fn": 0, + "current_judge_blind_holdout_fp": 0, + "current_judge_blind_holdout_fn": 0, + "baseline_judge_blind_holdout_fp": 0, + "baseline_judge_blind_holdout_fn": 0, + "winner_judge_blind_holdout_total_errors": 0, + "winner_adversarial_holdout_fp": 0, + "winner_adversarial_holdout_fn": 0, + "current_adversarial_holdout_fp": 0, + "current_adversarial_holdout_fn": 0, + "baseline_adversarial_holdout_fp": 0, + "baseline_adversarial_holdout_fn": 0, + "winner_adversarial_holdout_total_errors": 0, + "calibration": { + "holdout": { + "threshold": 0.48, + "mean_positive_score": 0.45, + "mean_non_trigger_score": 0.019, + "mean_near_neighbor_score": 0.037, + "min_positive_score": 0.08, + "max_non_trigger_score": 0.057, + "score_gap": 0.023, + "threshold_margin": -0.4, + "positive_threshold_buffer": -0.4, + "negative_threshold_buffer": 0.423, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "overlap" + }, + "blind_holdout": { + "threshold": 0.48, + "mean_positive_score": 0.82, + "mean_non_trigger_score": 0.059, + "mean_near_neighbor_score": 0.0, + "min_positive_score": 0.645, + "max_non_trigger_score": 0.228, + "score_gap": 0.417, + "threshold_margin": 0.165, + "positive_threshold_buffer": 0.165, + "negative_threshold_buffer": 0.252, + "boundary_case_count": 0, + "boundary_case_rate": 0.0, + "risk_band": "healthy" + }, + "adversarial_holdout": { + "threshold": 0.34, + "mean_positive_score": 0.907, + "mean_non_trigger_score": 0.083, + "mean_near_neighbor_score": 0.139, + "min_positive_score": 0.815, + "max_non_trigger_score": 0.277, + "score_gap": 0.538, + "threshold_margin": 0.063, + "positive_threshold_buffer": 0.475, + "negative_threshold_buffer": 0.063, + "boundary_case_count": 1, + "boundary_case_rate": 0.167, + "risk_band": "watch" + } + }, + "judge_blind": { + "winner": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "current": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + }, + "baseline": { + "agreement_rate": 1.0, + "mean_confidence": 0.677, + "rubric_version": "blind-v1" + } + }, + "family_health": { + "holdout": { + "family_count": 6, + "clean_family_count": 5, + "failing_families": [ + { + "family": "packet_assembly", + "errors": 1, + "pass_rate": 0.0 + } + ], + "weakest_family": { + "family": "packet_assembly", + "pass_rate": 0.0, + "errors": 1 + } + }, + "blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "judge_blind_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "blind_summary_only", + "pass_rate": 1.0, + "errors": 0 + } + }, + "adversarial_holdout": { + "family_count": 6, + "clean_family_count": 6, + "failing_families": [], + "weakest_family": { + "family": "adversarial_single_update_collision", + "pass_rate": 1.0, + "errors": 0 + } + } + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": true, + "promotion": { + "decision": "keep_current", + "winner_changed": false, + "all_gates_pass": true, + "route_clean": true, + "judge_agreement_ok": true, + "causes": [ + "no_candidate_outperformed_current", + "current_holdout_gap_present", + "current_holdout_risk", + "adversarial_watch_risk" + ], + "next_action": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "gate_status": { + "visible_holdout_non_regression": true, + "blind_holdout_non_regression": true, + "judge_blind_non_regression": true, + "judge_blind_agreement": true, + "adversarial_non_regression": true, + "adversarial_risk_ok": true, + "route_confusion_clean": true, + "family_stability": true + }, + "route_metrics": { + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.709, + "misroute_count": 0, + "ambiguous_case_count": 0 + } + }, + "registry": [ + { + "name": "governed-incident-command", + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, and stakeholder requirements. Use when asked to standardize incident review, severity assessment, incident communication, or incident packet assembly into one reusable operational skill. Do not use for generic debugging, one-off incident explanations, or brainstorming possible response ideas.", + "tokens": 93, + "dev_errors": 0, + "holdout_errors": 1 + }, + { + "name": "governed-incident-command", + "role": "current", + "label": "Current", + "ranking_state": "selected_by_dev", + "promotion_state": "kept_current", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 37, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Balanced", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 54, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Boundary", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Use when asked to standardize incident review, run severity assessment, or assemble incident communication. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Artifact Aware", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Trigger when requests mention alerts, incident notes, transcripts, or stakeholder requirements and the job is to standardize incident review, run severity assessment, or assemble incident communication.", + "tokens": 78, + "dev_errors": 0, + "holdout_errors": 1, + "reason_tags": [ + "longer_without_gain" + ] + }, + { + "name": "governed-incident-command", + "role": "candidate", + "label": "Guardrail", + "ranking_state": "not_selected", + "promotion_state": "blocked", + "description": "Build governed incident command packets from alerts, incident notes, transcripts, or stakeholder requirements. Do not use for generic debugging, one-off incident explanations, or drafting a single update.", + "tokens": 51, + "dev_errors": 1, + "holdout_errors": 1, + "reason_tags": [ + "weaker_dev_fit", + "longer_without_gain" + ] + } + ], + "decision": "keep_current" + } + ] + }, + "benchmark": { + "ok": true, + "query": "skills", + "source": "github-api", + "network_boundary": "github-api-only", + "repositories": [ + { + "full_name": "obra/superpowers", + "html_url": "https://github.com/obra/superpowers", + "description": "An agentic skills framework & software development methodology that works.", + "stars": 226125, + "topics": [], + "primary_language": "Shell", + "patterns": [ + { + "category": "method", + "matched": "process", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale." + }, + { + "category": "execution", + "matched": "cli", + "borrow": "Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not copy repo-specific commands or environment assumptions verbatim." + }, + { + "category": "evaluation", + "matched": "test", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here." + }, + { + "category": "structure", + "matched": "docs", + "borrow": "Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not mirror documentation bulk that adds context cost without improving reliability." + } + ], + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure.", + "Borrow explicit validation and quality gates that make iteration safer." + ], + "avoid": [ + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim.", + "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here." + ], + "readme_excerpt": "# Superpowers\n\nSuperpowers is a complete software development methodology for your coding agents, built on top of a set of composable skills and some initial instructions that make sure your agent uses them.\n\n## Quickstart\n\nGive your agent Superpowers: [Claude Code](#claude-code), [Codex CLI](#codex-cli), [Codex App](#codex-app), [Factory Droid](#factory-droid), [Gemini CLI](#gemini-cli), [OpenCode](#opencode), [Cursor](#cursor), [GitHub Copilot CLI](#github-copilot-cli).\n\n## How it works\n\nIt starts from the moment you fire up your coding agent. As soon as it sees that you're building something, it *doesn't* just jump into trying to write code. Instead, it steps back and asks you what you're really trying to do. \n\nOnce it's teased a spec out of the conversation, it shows it to you in chunks short enough to actually read and digest. \n\nAfter you've signed off on the design, your agent puts together an implementation plan that's clear enough for an enthusiastic junior engineer with poor taste, no judgement, no project context, and an aversion to testing to follow. It emphasizes true red/green TDD, YAGNI (You Aren't Gonna Need It), and DRY. \n\nNext up, once you say \"go\", it launches a *" + }, + { + "full_name": "affaan-m/ECC", + "html_url": "https://github.com/affaan-m/ECC", + "description": "The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.", + "stars": 214381, + "topics": [ + "ai-agents", + "anthropic", + "claude", + "claude-code", + "developer-tools", + "llm", + "mcp", + "productivity" + ], + "primary_language": "JavaScript", + "patterns": [ + { + "category": "method", + "matched": "workflow", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale." + }, + { + "category": "execution", + "matched": "command, shell, script", + "borrow": "Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not copy repo-specific commands or environment assumptions verbatim." + }, + { + "category": "structure", + "matched": "guide, docs", + "borrow": "Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not mirror documentation bulk that adds context cost without improving reliability." + }, + { + "category": "governance", + "matched": "security, review", + "borrow": "Borrow the explicit review, safety, or operational trust boundaries.", + "avoid": "Do not import compliance-heavy process where the new skill does not need it." + } + ], + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure.", + "Borrow the way it separates explanation, examples, and reusable structure." + ], + "avoid": [ + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim.", + "Do not mirror documentation bulk that adds context cost without improving reliability." + ], + "readme_excerpt": "**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md) | [ไทย](docs/th/README.md) | [Deutsch](docs/de-DE/README.md) | [Español](docs/es/README.md)\n\n![ECC — the agent harness operating system](assets/hero.png)\n\n[![Stars](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.ecc.tools%2Fbadge%2Fstars&style=flat)](https://github.com/affaan-m/ECC/stargazers)\n[![Forks](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.ecc.tools%2Fbadge%2Fforks&style=flat)](https://github.com/affaan-m/ECC/network/members)\n[![Contributors](https://img.shields.io/github/contributors/affaan-m/ECC?style=flat)](https://github.com/affaan-m/ECC/graphs/contributors)\n[![npm ecc-universal](https://img.shields.io/npm/dw/ecc-universal?label=ecc-universal%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-universal)\n[![npm ecc-agentshield](https://img.shields.io/npm/dw/ecc-agentshield?label=ecc-agentshield%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-agen" + }, + { + "full_name": "multica-ai/andrej-karpathy-skills", + "html_url": "https://github.com/multica-ai/andrej-karpathy-skills", + "description": "A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathy's observations on LLM coding pitfalls.", + "stars": 174264, + "topics": [], + "primary_language": null, + "patterns": [ + { + "category": "evaluation", + "matched": "test, validation", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here." + }, + { + "category": "structure", + "matched": "guide", + "borrow": "Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not mirror documentation bulk that adds context cost without improving reliability." + }, + { + "category": "portability", + "matched": "plugin", + "borrow": "Borrow how it keeps core semantics stable across environments or integrations.", + "avoid": "Do not inherit platform lock-in or vendor-specific branching unless truly required." + } + ], + "borrow": [ + "Borrow explicit validation and quality gates that make iteration safer.", + "Borrow the way it separates explanation, examples, and reusable structure.", + "Borrow how it keeps core semantics stable across environments or integrations." + ], + "avoid": [ + "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "Do not mirror documentation bulk that adds context cost without improving reliability.", + "Do not inherit platform lock-in or vendor-specific branching unless truly required." + ], + "readme_excerpt": "# Karpathy-Inspired Claude Code Guidelines\n\n> Check out my new project [Multica](https://github.com/multica-ai/multica) — an open-source platform for running and managing coding agents with reusable skills.\n>\n> Follow me on X: [https://x.com/jiayuan_jy](https://x.com/jiayuan_jy)\n\nA single `CLAUDE.md` file to improve Claude Code behavior, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.\n\nEnglish | [简体中文](./README.zh.md)\n\n## The Problems\n\nFrom Andrej's post:\n\n> \"The models make wrong assumptions on your behalf and just run along with them without checking. They don't manage their confusion, don't seek clarifications, don't surface inconsistencies, don't present tradeoffs, don't push back when they should.\"\n\n> \"They really like to overcomplicate code and APIs, bloat abstractions, don't clean up dead code... implement a bloated construction over 1000 lines when 100 would do.\"\n\n> \"They still sometimes change/remove comments and code they don't sufficiently understand as side effects, even if orthogonal to the task.\"\n\n## The Solution\n\nFour principles in one file that directly address these issues:\n\n| Principle | Addr" + } + ], + "cross_repo": { + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure.", + "Borrow explicit validation and quality gates that make iteration safer.", + "Borrow the way it separates explanation, examples, and reusable structure.", + "Borrow how it keeps core semantics stable across environments or integrations.", + "Ask the user which of these patterns feels most worth borrowing before freezing the package shape." + ], + "avoid": [ + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim.", + "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "Do not mirror documentation bulk that adds context cost without improving reliability.", + "Do not inherit platform lock-in or vendor-specific branching unless truly required." + ] + }, + "external_references": [ + { + "name": "obra/superpowers", + "category": "method", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "source": "external" + }, + { + "name": "affaan-m/ECC", + "category": "method", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "source": "external" + }, + { + "name": "multica-ai/andrej-karpathy-skills", + "category": "evaluation", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "source": "external" + } + ], + "borrow_prompt": "I found 3 public GitHub projects worth studying. Do you want to borrow any of these patterns for method, structure, execution, or portability?", + "warnings": [] + }, + "reference_synthesis": { + "skill_name": "yao-meta-skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "intent_confidence": { + "score": 100, + "band": "high", + "gate_passed": true + }, + "github_benchmarks": [ + { + "name": "obra/superpowers", + "url": "https://github.com/obra/superpowers", + "stars": 226125, + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure." + ] + }, + { + "name": "affaan-m/ECC", + "url": "https://github.com/affaan-m/ECC", + "stars": 214381, + "borrow": [ + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure." + ] + }, + { + "name": "multica-ai/andrej-karpathy-skills", + "url": "https://github.com/multica-ai/andrej-karpathy-skills", + "stars": 174264, + "borrow": [ + "Borrow explicit validation and quality gates that make iteration safer.", + "Borrow the way it separates explanation, examples, and reusable structure." + ] + } + ], + "source_tracks": [ + { + "source_type": "official", + "name": "Official skill anatomy and context discipline", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "metadata", + "references" + ], + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "why_relevant": "This track matches: metadata, references." + }, + { + "source_type": "research", + "name": "Hypothesis-test-learn loop", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "test", + "iteration" + ], + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "why_relevant": "This track matches: test, iteration." + }, + { + "source_type": "principles", + "name": "Boundary-first design", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "route", + "trigger", + "exclude" + ], + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "why_relevant": "This track matches: route, trigger, exclude." + } + ], + "synthesis": { + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package.", + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure." + ], + "avoid_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier.", + "Do not expand execution assets until route boundaries stay clean.", + "Do not import process overhead that only exists for that project's scale.", + "Do not copy repo-specific commands or environment assumptions verbatim." + ], + "quality_risers": [ + "Use GitHub repositories for concrete package and workflow patterns.", + "Use curated official or commercial tracks for entrypoint and operator ergonomics.", + "Use research tracks to justify the smallest evaluation loop that still catches regressions.", + "Use principle tracks to keep the package small, boundary-aware, and outcome-driven." + ], + "pattern_gate": { + "threshold": 4, + "source_count": 6, + "accepted": [ + { + "name": "Official skill anatomy and context discipline", + "source_type": "official", + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "Hypothesis-test-learn loop", + "source_type": "research", + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "Boundary-first design", + "source_type": "principles", + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "obra/superpowers", + "source_type": "github", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.; Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + }, + { + "name": "affaan-m/ECC", + "source_type": "github", + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.; Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + "gates": { + "recurrence": true, + "generativity": true, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "generativity", + "distinctiveness", + "boundary" + ], + "missing": [], + "score": 4 + } + ], + "deferred": [ + { + "name": "multica-ai/andrej-karpathy-skills", + "source_type": "github", + "borrow": "Borrow explicit validation and quality gates that make iteration safer.; Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + "gates": { + "recurrence": true, + "generativity": false, + "distinctiveness": true, + "boundary": true + }, + "passed": [ + "recurrence", + "distinctiveness", + "boundary" + ], + "missing": [ + "generativity" + ], + "score": 3 + } + ], + "summary": "5 accepted, 1 deferred using threshold 4/4." + }, + "conflicts": [ + { + "key": "lightweight_vs_governance", + "summary": "The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_preference": "lightweight or speed-first", + "benchmark_pressure": "governance or evaluation-heavy patterns" + } + ], + "recommendation": { + "summary": "Start by borrowing this pattern: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. Avoid this for the first pass: Do not let packaging or platform concerns swallow the core job boundary.", + "borrow_now": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed." + ], + "avoid_for_now": [ + "Do not let packaging or platform concerns swallow the core job boundary.", + "Do not create experimental overhead that exceeds the skill's real risk tier." + ], + "why": "There is a real design conflict to resolve: The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_decision_required": true + }, + "visibility": { + "mode": "explicit", + "user_decision_required": true, + "reasons": [ + "design_conflict" + ], + "user_note": "Surface the recommendation because intent is still settling or there is a real design conflict that needs a user call.", + "reviewer_note": "Keep the full benchmark and synthesis evidence visible for authors and reviewers." + }, + "decision_prompt": "Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call.", + "source_mix": { + "github_benchmarks": 3, + "curated_tracks": 3, + "user_references": 0 + } + } + }, + "output_risk": { + "skill_name": "yao-meta-skill", + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "top_risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap." + ], + "output_constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output." + ], + "self_repair_checks": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards.", + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note.", + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ], + "reviewer_note": "Use this report before deepening the package and again before approving example outputs." + }, + "artifact_design": { + "skill_name": "yao-meta-skill-trigger-coverage-merge", + "design_system": "metric editorial", + "primary_artifact": { + "key": "dashboard", + "label": "Dashboard or metrics page", + "direction": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "matched_keywords": [ + "metric", + "score", + "table", + "scorecard" + ] + }, + "artifact_families": [ + { + "key": "dashboard", + "label": "Dashboard or metrics page", + "score": 4, + "matched_keywords": [ + "metric", + "score", + "table", + "scorecard" + ], + "direction": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation." + }, + { + "key": "review_viewer", + "label": "Review viewer", + "score": 3, + "matched_keywords": [ + "review", + "viewer", + "audit" + ], + "direction": "Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix." + }, + { + "key": "code_or_cli", + "label": "Code, CLI, or implementation guide", + "score": 3, + "matched_keywords": [ + "code", + "script", + "command" + ], + "direction": "Execution-focused technical artifact with environment assumptions, copyable commands, expected outputs, and side effects made explicit." + }, + { + "key": "report", + "label": "Report or brief", + "score": 2, + "matched_keywords": [ + "report", + "summary" + ], + "direction": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail." + }, + { + "key": "visual_capture", + "label": "Screenshot or visual evidence", + "score": 1, + "matched_keywords": [ + "screenshot" + ], + "direction": "Evidence-led visual artifact that records source, viewport, crop intent, and the exact region the reader should inspect." + } + ], + "layout_patterns": [ + "metric board", + "ranked signals", + "comparison rows", + "interpretation", + "action queue", + "summary", + "variant comparison", + "evidence" + ], + "design_tokens": { + "type": [ + "Use a distinctive display face or serif for major claims when the artifact is editorial.", + "Use a restrained sans for dense body text and technical details.", + "Use mono only for metadata, paths, commands, labels, and evidence tags." + ], + "color": [ + "Choose colors from the artifact's domain, brand, or evidence mood.", + "Do not default to Kami parchment, purple gradients, or generic SaaS blue unless the content justifies it.", + "Keep accent color limited to decisions, active states, risk, or section anchors." + ], + "spacing": [ + "Prefer clear grid rhythm over floating decorative cards.", + "Increase whitespace around decisions and shrink whitespace around supporting metadata.", + "Split dense content instead of shrinking type or adding scroll traps." + ], + "components": [ + "Use cards for grouped evidence, tables for comparisons, callouts for decisions, and timelines for sequence.", + "Avoid cards inside cards.", + "Keep reviewer-only detail visible but visually quieter than user-facing guidance." + ] + }, + "quality_gates": [ + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities.", + "Make differences visible instead of hiding them in prose.", + "Separate author-facing recommendations from reviewer-only evidence.", + "Surface conflicts clearly and keep routine benchmark synthesis quiet.", + "Name the working directory and required inputs for commands.", + "Mark destructive, networked, or external side-effect operations." + ], + "anti_patterns": [ + "Do not copy Kami's fixed parchment background as a default.", + "Do not use generic purple gradients, glass cards, or stock SaaS hero sections unless the content calls for them.", + "Do not let Markdown tables become the default shape for every comparison or explanation.", + "Do not turn reviewer evidence into user-facing clutter.", + "Do not invent screenshots, citations, charts, or UI states." + ], + "reviewer_note": "Use this profile to judge whether the generated artifacts feel designed for their job, not merely rendered." + }, + "prompt_quality": { + "skill_name": "yao-meta-skill-trigger-coverage-merge", + "relevance": "prompt-aware", + "primary_task_family": { + "key": "execution_operation", + "label": "Execution operation", + "matched_keywords": [ + "workflow", + "runbook" + ] + }, + "task_families": [ + { + "key": "execution_operation", + "label": "Execution operation", + "score": 2, + "matched_keywords": [ + "workflow", + "runbook" + ], + "role_guidance": "Use an operator role with explicit boundaries, inputs, outputs, and failure handling.", + "task_guidance": "Convert the job into ordered steps with validation checks and stop conditions.", + "format_guidance": "Return a runbook-like handoff with commands, checks, owners, and next actions when relevant." + }, + { + "key": "creative_generation", + "label": "Creative generation", + "score": 1, + "matched_keywords": [ + "content" + ], + "role_guidance": "Use a taste-aware creator role with clear audience, tone, and originality boundaries.", + "task_guidance": "Generate variants, explain selection logic, and preserve the user's distinctive constraints.", + "format_guidance": "Return options with rationale, selection criteria, and refinement paths." + }, + { + "key": "analytical_reasoning", + "label": "Analytical reasoning", + "score": 1, + "matched_keywords": [ + "decision" + ], + "role_guidance": "Use an analyst role that separates evidence, inference, uncertainty, and recommendation.", + "task_guidance": "State assumptions, compare alternatives, and make the decision path inspectable.", + "format_guidance": "Return findings, evidence, tradeoffs, recommendation, and residual risks." + }, + { + "key": "dialogue_interaction", + "label": "Dialogue interaction", + "score": 1, + "matched_keywords": [ + "dialogue" + ], + "role_guidance": "Use a conversational role that asks only high-leverage questions and remembers the user's goal.", + "task_guidance": "Clarify intent, resolve uncertainty, and converge toward a recommendation instead of a long option list.", + "format_guidance": "Return concise prompts, decision points, and reviewer-visible assumptions." + } + ], + "complexity": { + "band": "expert", + "score": 27, + "reason": "multiple task families plus governance, evaluation, or expert-level constraints" + }, + "need_model": { + "explicit_need": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "implicit_need": "The reusable skill needs a stable role, task, and output contract rather than a one-off prompt.", + "scenario": "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions, an existing skill directory that needs refactor, evaluation, packaging, or governance hardening, target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution, benchmark references, local constraints, desired maturity tier, and review standards", + "user_level": "infer from examples and standards; ask only if it changes output depth", + "success_standard": "trigger boundaries must be tested with should-trigger and should-not-trigger cases, production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence, governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation, generated reports should be bilingual or reviewer-friendly when they are user-facing, each new asset must earn its place by reducing ambiguity, risk, or repeated work" + }, + "rtf_to_skill": { + "role": "Use an operator role with explicit boundaries, inputs, outputs, and failure handling.", + "task": "Convert the job into ordered steps with validation checks and stop conditions.", + "format": "Return a runbook-like handoff with commands, checks, owners, and next actions when relevant." + }, + "quality_matrix": [ + { + "key": "completeness", + "label": "Completeness", + "score": 100, + "matched_signals": [ + "input", + "output", + "constraint", + "standard" + ], + "repair": "Name missing inputs, outputs, constraints, or success standards before deepening the package." + }, + { + "key": "clarity", + "label": "Clarity", + "score": 85, + "matched_signals": [ + "clear" + ], + "repair": "Replace broad verbs with observable actions and define what done means." + }, + { + "key": "consistency", + "label": "Consistency", + "score": 90, + "matched_signals": [ + "aligned", + "boundary" + ], + "repair": "Check that role, task, format, exclusions, and examples do not contradict each other." + }, + { + "key": "practicality", + "label": "Practicality", + "score": 90, + "matched_signals": [ + "use", + "workflow" + ], + "repair": "Add runnable steps, examples, or verification cues instead of abstract advice." + }, + { + "key": "specificity", + "label": "Specificity", + "score": 80, + "matched_signals": [], + "repair": "Anchor wording in the user's audience, domain nouns, and target outcome." + } + ], + "overall_quality_score": 89.0, + "self_repair_checks": [ + "Check explicit need, implicit need, scenario, user level, and success standard before deepening.", + "Map Role, Task, and Format into skill behavior, not decorative prompt labels.", + "Ask one focused clarification only when missing information changes the package boundary.", + "Add tests or examples for prompt-heavy behavior before treating it as reusable.", + "Keep prompt methodology in references and reports instead of bloating SKILL.md." + ], + "reviewer_note": "Use this profile when the package depends on prompt behavior, role design, output contracts, or conversation quality." + }, + "evidence_readiness": { + "score": 85, + "checks": [ + { + "label": "Intent clarity", + "status": "ready", + "detail": "100/100 intent confidence." + }, + { + "label": "Benchmark coverage", + "status": "ready", + "detail": "3 GitHub benchmark repositories attached." + }, + { + "label": "Pattern gate", + "status": "ready", + "detail": "5 accepted, 1 deferred using threshold 4/4." + }, + { + "label": "Conflict handling", + "status": "decision needed", + "detail": "The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure." + }, + { + "label": "Output risk profile", + "status": "ready", + "detail": "5 output risk families attached." + }, + { + "label": "Artifact design profile", + "status": "ready", + "detail": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation." + }, + { + "label": "Prompt quality profile", + "status": "ready", + "detail": "89.0/100 prompt-facing quality score." + } + ], + "reviewer_note": "Use this section to decide whether the package is ready to deepen or should stay in discovery." + } +} \ No newline at end of file diff --git a/reports/review_annotations.json b/reports/review_annotations.json new file mode 100644 index 0000000..66f2b1d --- /dev/null +++ b/reports/review_annotations.json @@ -0,0 +1,24 @@ +{ + "schema_version": "1.0", + "ok": true, + "skill_dir": ".", + "source": "tests/tmp_review_studio/empty_review_annotations_input.json", + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [], + "template_written": false, + "artifacts": { + "json": "reports/review_annotations.json", + "markdown": "reports/review_annotations.md" + } +} diff --git a/reports/review_annotations.md b/reports/review_annotations.md new file mode 100644 index 0000000..68230eb --- /dev/null +++ b/reports/review_annotations.md @@ -0,0 +1,19 @@ +# Review Annotations + +This report renders reviewer annotations attached to Review Studio gates and source/report paths. + +- Annotations: `0` +- Open: `0` +- Resolved: `0` +- Deferred: `0` +- Open blockers: `0` +- Open warnings: `0` + +No reviewer annotations recorded yet. + + +## Review Rule + +- Use annotations for reviewer comments tied to a gate or source line. +- Use waivers only for explicit acceptance of warning-level release risk. +- Open blocker annotations should block a release decision until resolved or deferred with rationale. diff --git a/reports/review_waivers.json b/reports/review_waivers.json new file mode 100644 index 0000000..f39abd1 --- /dev/null +++ b/reports/review_waivers.json @@ -0,0 +1,120 @@ +{ + "schema_version": "1.0", + "ok": true, + "skill_dir": ".", + "generated_at": "2026-06-13", + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 2, + "waiverable_open_count": 1, + "non_waivable_count": 1 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "review_studio_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "review-waivers", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "world-class-evidence" + ], + "known_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "waiverable_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "non_waivable_gate_keys": [ + "review-waivers", + "world-class-evidence" + ] + }, + "waivers": [], + "waiver_candidates": [ + { + "gate_key": "output-lab", + "label": "Output Lab", + "status": "needs-reviewer-decision", + "waiver_allowed": true, + "decision_options": [ + "accepted-risk", + "false-positive", + "temporary-exception" + ], + "risk_summary": "review pending 5; model-executed 10; output failures 0", + "required_review": [ + "Reviewer confirms this release does not claim provider-backed or human-adjudicated output superiority.", + "Reviewer names the release scope and expiry date.", + "Reviewer links output_review_adjudication or output_execution evidence." + ], + "suggested_evidence": "reports/output_review_adjudication.md", + "suggested_command": "python3 scripts/yao.py review-waivers . --add-waiver --gate-key output-lab --reviewer \"\" --reason \"Output Lab has pending human/provider evidence; accepted only for this bounded review scope.\" --expires-at 2027-06-13 --evidence reports/output_review_adjudication.md", + "world_class_boundary": "Does not count as provider, human, or public world-class completion evidence." + }, + { + "gate_key": "world-class-evidence", + "label": "World-Class Evidence", + "status": "cannot-waive", + "waiver_allowed": false, + "risk_summary": "4 pending evidence entries; 1 human pending; 3 external pending", + "required_review": [ + "Do not use a waiver to claim public world-class readiness.", + "Either submit accepted ledger evidence or state that this release does not claim world-class completion.", + "Keep claim guard active until ledger summary.ready_to_claim_world_class is true." + ], + "suggested_evidence": "reports/world_class_evidence_ledger.md", + "suggested_command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard .", + "world_class_boundary": "Non-waivable completion boundary." + } + ], + "failures": [], + "warnings": [], + "artifacts": { + "json": "reports/review_waivers.json", + "markdown": "reports/review_waivers.md" + } +} diff --git a/reports/review_waivers.md b/reports/review_waivers.md new file mode 100644 index 0000000..e70204a --- /dev/null +++ b/reports/review_waivers.md @@ -0,0 +1,72 @@ +# Review Waivers + +- OK: `True` +- Waivers: `0` +- Active: `0` +- Expired: `0` +- Invalid: `0` +- Covered gates: `none` +- Waiver candidates: `2` +- Open waiverable candidates: `1` +- Non-waivable boundaries: `1` + +## Policy + +- Blocker waivers allowed: `False` +- Minimum reason chars: `20` +- Expiry is required for every waiver. +- World-class evidence completion cannot be waived; it can only be proven by accepted ledger evidence. +- Review Studio gates: `architecture-maintainability, context-budget, intent-canvas, operations-loop, output-lab, permission-gates, permission-runtime, python-compat, registry-audit, release-notes, review-waivers, runtime-matrix, skill-atlas, trigger-lab, trust-report, world-class-evidence` +- Waiverable gates: `architecture-maintainability, context-budget, intent-canvas, operations-loop, output-lab, permission-gates, permission-runtime, python-compat, registry-audit, release-notes, runtime-matrix, skill-atlas, trigger-lab, trust-report` +- Non-waivable gates: `review-waivers, world-class-evidence` + +## Waivers + +- None + +## Candidate Actions + +| Gate | Status | Waiver | Risk | Evidence | +| --- | --- | --- | --- | --- | +| `output-lab` | `needs-reviewer-decision` | `true` | review pending 5; model-executed 10; output failures 0 | `reports/output_review_adjudication.md` | +| `world-class-evidence` | `cannot-waive` | `false` | 4 pending evidence entries; 1 human pending; 3 external pending | `reports/world_class_evidence_ledger.md` | + +### Output Lab + +- gate: `output-lab` +- status: `needs-reviewer-decision` +- waiver allowed: `true` +- risk: review pending 5; model-executed 10; output failures 0 +- evidence: `reports/output_review_adjudication.md` +- verification: `python3 scripts/yao.py review-waivers . --add-waiver --gate-key output-lab --reviewer "" --reason "Output Lab has pending human/provider evidence; accepted only for this bounded review scope." --expires-at 2027-06-13 --evidence reports/output_review_adjudication.md` +- world-class boundary: Does not count as provider, human, or public world-class completion evidence. + +#### Required Review + +- Reviewer confirms this release does not claim provider-backed or human-adjudicated output superiority. +- Reviewer names the release scope and expiry date. +- Reviewer links output_review_adjudication or output_execution evidence. + +### World-Class Evidence + +- gate: `world-class-evidence` +- status: `cannot-waive` +- waiver allowed: `false` +- risk: 4 pending evidence entries; 1 human pending; 3 external pending +- evidence: `reports/world_class_evidence_ledger.md` +- verification: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard .` +- world-class boundary: Non-waivable completion boundary. + +#### Required Review + +- Do not use a waiver to claim public world-class readiness. +- Either submit accepted ledger evidence or state that this release does not claim world-class completion. +- Keep claim guard active until ledger summary.ready_to_claim_world_class is true. + +## Failures + +- None + +## Warnings + +- None diff --git a/reports/route_scorecard.json b/reports/route_scorecard.json new file mode 100644 index 0000000..e5bb5ba --- /dev/null +++ b/reports/route_scorecard.json @@ -0,0 +1,593 @@ +{ + "summary": { + "total_cases": 13, + "correct_cases": 13, + "accuracy": 1.0, + "ambiguous_case_count": 0, + "margin_warning_threshold": 0.08, + "misroute_count": 0, + "no_route_accuracy": 1.0 + }, + "route_stats": { + "yao-meta-skill": { + "expected_count": 3, + "predicted_count": 3, + "correct_count": 3, + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.644 + }, + "team-frontend-review": { + "expected_count": 3, + "predicted_count": 3, + "correct_count": 3, + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.805 + }, + "governed-incident-command": { + "expected_count": 3, + "predicted_count": 3, + "correct_count": 3, + "precision": 1.0, + "recall": 1.0, + "average_margin": 0.709 + }, + "no_route": { + "expected_count": 4, + "predicted_count": 4, + "correct_count": 4, + "precision": 1.0, + "recall": 1.0, + "average_margin": null + } + }, + "confusion_matrix": { + "yao-meta-skill": { + "yao-meta-skill": 3, + "team-frontend-review": 0, + "governed-incident-command": 0, + "no_route": 0 + }, + "team-frontend-review": { + "yao-meta-skill": 0, + "team-frontend-review": 3, + "governed-incident-command": 0, + "no_route": 0 + }, + "governed-incident-command": { + "yao-meta-skill": 0, + "team-frontend-review": 0, + "governed-incident-command": 3, + "no_route": 0 + }, + "no_route": { + "yao-meta-skill": 0, + "team-frontend-review": 0, + "governed-incident-command": 0, + "no_route": 4 + } + }, + "ambiguous_cases": [], + "misroutes": [], + "cases": [ + { + "family": "meta_create", + "prompt": "Create a reusable skill from this workflow and add evals before sharing it with the team.", + "expected_route": "yao-meta-skill", + "predicted_route": "yao-meta-skill", + "correct": true, + "margin": 0.681, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.681, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package", + "transform_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "meta_iterate", + "prompt": "Refactor this existing skill description, tighten the trigger boundary, and package it for reuse.", + "expected_route": "yao-meta-skill", + "predicted_route": "yao-meta-skill", + "correct": true, + "margin": 0.51, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.51, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "eval_optimize", + "iterate_existing_skill", + "reuse_package" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.011, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "meta_operationalize", + "prompt": "Turn these prompts, notes, and transcripts into a maintained skill package with references and scripts.", + "expected_route": "yao-meta-skill", + "predicted_route": "yao-meta-skill", + "correct": true, + "margin": 0.74, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.74, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "build_skill", + "multi_asset", + "reuse_package", + "transform_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.181, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [ + "governed_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.011, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "frontend_premerge", + "prompt": "Run a pre-merge frontend review for keyboard navigation issues and unsafe rendering.", + "expected_route": "team-frontend-review", + "predicted_route": "team-frontend-review", + "correct": true, + "margin": 0.893, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.893, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "ui_security", + "ux_regression" + ], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.012, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.006, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "frontend_states", + "prompt": "Run a pre-merge frontend review on this React diff for missing empty states, form handling, and UX regressions.", + "expected_route": "team-frontend-review", + "predicted_route": "team-frontend-review", + "correct": true, + "margin": 0.704, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.704, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "frontend_scope", + "review_frontend", + "ui_security", + "ux_regression" + ], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.008, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.004, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "frontend_a11y", + "prompt": "Review React components for a11y regressions, unsafe rendering, and UI behavior before merge.", + "expected_route": "team-frontend-review", + "predicted_route": "team-frontend-review", + "correct": true, + "margin": 0.819, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.819, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "accessibility", + "frontend_scope", + "review_frontend", + "ui_security" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.006, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.006, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "incident_packet", + "prompt": "Build a governed incident command packet from alerts, responder notes, incident communication needs, and severity findings.", + "expected_route": "governed-incident-command", + "predicted_route": "governed-incident-command", + "correct": true, + "margin": 1.0, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 1.0, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.011, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.005, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "incident_severity", + "prompt": "Run severity assessment and package incident communication from this outage review.", + "expected_route": "governed-incident-command", + "predicted_route": "governed-incident-command", + "correct": true, + "margin": 0.505, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 0.505, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "comms_and_owners", + "severity_assessment" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.179, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [ + "reuse_package" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.022, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "incident_governed_workflow", + "prompt": "Standardize this incident review into a reusable governed workflow with owner actions.", + "expected_route": "governed-incident-command", + "predicted_route": "governed-incident-command", + "correct": true, + "margin": 0.621, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 1.0, + "passed_threshold": true, + "threshold": 0.48, + "matched_desired_concepts": [ + "comms_and_owners", + "governed_workflow", + "incident_packet", + "severity_assessment" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.379, + "passed_threshold": true, + "threshold": 0.33, + "matched_desired_concepts": [ + "reuse_package", + "transform_workflow" + ], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.013, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "no_route_explain", + "prompt": "Explain keyboard accessibility basics in plain English.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "team-frontend-review", + "score": 0.195, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [ + "accessibility" + ], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [ + "explain_only" + ] + }, + { + "name": "governed-incident-command", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "no_route_build", + "prompt": "Build a React form page with loading and empty states.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 0.008, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [ + "frontend_scope" + ], + "matched_negative_concepts": [ + "build_ui" + ] + } + ] + }, + { + "family": "no_route_single_update", + "prompt": "Write one short outage update for customers from these notes.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "yao-meta-skill", + "score": 0.016, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.008, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "governed-incident-command", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + } + ] + }, + { + "family": "no_route_document_only", + "prompt": "Summarize this workflow and keep it as documentation only.", + "expected_route": "no_route", + "predicted_route": "no_route", + "correct": true, + "margin": null, + "ambiguous": false, + "ranked_routes": [ + { + "name": "governed-incident-command", + "score": 0.01, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "team-frontend-review", + "score": 0.009, + "passed_threshold": false, + "threshold": 0.48, + "matched_desired_concepts": [], + "matched_negative_concepts": [] + }, + { + "name": "yao-meta-skill", + "score": 0.0, + "passed_threshold": false, + "threshold": 0.33, + "matched_desired_concepts": [ + "transform_workflow" + ], + "matched_negative_concepts": [ + "no_build_directive", + "summarize_only" + ] + } + ] + } + ] +} diff --git a/reports/route_scorecard.md b/reports/route_scorecard.md new file mode 100644 index 0000000..61d1be2 --- /dev/null +++ b/reports/route_scorecard.md @@ -0,0 +1,30 @@ +# Route Scorecard + +- total cases: `13` +- accuracy: `1.0` +- ambiguous cases: `0` +- no-route accuracy: `1.0` + +## Route Metrics + +| Route | Expected | Predicted | Precision | Recall | Avg Margin | +| --- | ---: | ---: | ---: | ---: | ---: | +| `yao-meta-skill` | 3 | 3 | 1.0 | 1.0 | 0.644 | +| `team-frontend-review` | 3 | 3 | 1.0 | 1.0 | 0.805 | +| `governed-incident-command` | 3 | 3 | 1.0 | 1.0 | 0.709 | +| `no_route` | 4 | 4 | 1.0 | 1.0 | - | + +## Confusion Matrix + +| Expected \ Predicted | `yao-meta-skill` | `team-frontend-review` | `governed-incident-command` | `no_route` | +| --- | ---: | ---: | ---: | ---: | +| `yao-meta-skill` | 3 | 0 | 0 | 0 | +| `team-frontend-review` | 0 | 3 | 0 | 0 | +| `governed-incident-command` | 0 | 0 | 3 | 0 | +| `no_route` | 0 | 0 | 0 | 4 | + +## Ambiguous Cases + +| Family | Expected | Predicted | Margin | +| --- | --- | --- | ---: | +| - | - | - | - | diff --git a/reports/runtime_permission_probes.json b/reports/runtime_permission_probes.json new file mode 100644 index 0000000..59c9a1a --- /dev/null +++ b/reports/runtime_permission_probes.json @@ -0,0 +1,391 @@ +{ + "schema_version": "1.0", + "ok": true, + "skill_dir": ".", + "package_dir": "tests/tmp_review_studio/dist", + "expected_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "summary": { + "target_count": 4, + "pass_count": 4, + "fail_count": 0, + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "residual_risk_count": 4, + "required_capability_count": 3, + "failure_count": 0, + "installer_enforcement_source_status": "present", + "installer_enforcement_target_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "installer_permission_capability_count": 3, + "world_class_native_evidence_ready": false, + "installer_enforcement_ready": true + }, + "installer_enforcement": { + "source": "reports/install_simulation.json", + "source_status": "present", + "package_dir_matches": true, + "summary": { + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "failure_count": 0 + } + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/openai/adapter.json", + "permission_model": "metadata-only", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "openai", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "openai adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "openai adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "openai adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "openai permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "openai target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "openai target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "openai target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "openai target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "openai target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "openai review_required matches whether capabilities are required" + }, + { + "key": "openai-yaml-present", + "passed": true, + "detail": "OpenAI permission metadata YAML is readable" + }, + { + "key": "openai-yaml-permissions", + "passed": true, + "detail": "OpenAI YAML permission contract mirrors expected capabilities" + }, + { + "key": "openai-yaml-native-flag", + "passed": true, + "detail": "OpenAI YAML declares native_enforcement as a boolean" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "claude", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/claude/adapter.json", + "permission_model": "neutral-source-plus-adapter", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "claude", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "claude adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "claude adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "claude adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "claude permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "claude target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "claude target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "claude target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "claude target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "claude target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "claude review_required matches whether capabilities are required" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "generic", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/generic/adapter.json", + "permission_model": "agent-skills-compatible-metadata", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "generic", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "generic adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "generic adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "generic adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "generic permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "generic target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "generic target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "generic target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "generic target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "generic target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "generic review_required matches whether capabilities are required" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "vscode", + "status": "pass", + "adapter": "tests/tmp_review_studio/dist/targets/vscode/adapter.json", + "permission_model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "installer_enforcement": { + "target": "vscode", + "source_status": "present", + "enforced": true, + "enforced_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "failure_details": [] + }, + "assurance": "metadata-fallback-explicit", + "declared_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "checks": [ + { + "key": "adapter-present", + "passed": true, + "detail": "vscode adapter.json is readable" + }, + { + "key": "permission-contract-present", + "passed": true, + "detail": "vscode adapter includes permission_contract" + }, + { + "key": "target-contract-present", + "passed": true, + "detail": "vscode adapter includes target_permission_contract" + }, + { + "key": "source-available", + "passed": true, + "detail": "vscode permission_contract links to an available trust report" + }, + { + "key": "declared-capabilities-match", + "passed": true, + "detail": "vscode target_permission_contract mirrors expected capabilities" + }, + { + "key": "capability-counts-present", + "passed": true, + "detail": "vscode target_permission_contract includes capability_counts" + }, + { + "key": "native-enforcement-boolean", + "passed": true, + "detail": "vscode target_permission_contract declares native_enforcement as a boolean" + }, + { + "key": "representation-present", + "passed": true, + "detail": "vscode target_permission_contract declares where permission metadata is represented" + }, + { + "key": "operator-note-present", + "passed": true, + "detail": "vscode target_permission_contract includes an operator_note" + }, + { + "key": "review-required-matches", + "passed": true, + "detail": "vscode review_required matches whether capabilities are required" + } + ], + "failures": [], + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + } + ], + "failures": [], + "artifacts": { + "json": "reports/runtime_permission_probes.json", + "markdown": "reports/runtime_permission_probes.md" + } +} diff --git a/reports/runtime_permission_probes.md b/reports/runtime_permission_probes.md new file mode 100644 index 0000000..5e242e9 --- /dev/null +++ b/reports/runtime_permission_probes.md @@ -0,0 +1,40 @@ +# Runtime Permission Probes + +Runtime permission probes verify that generated target adapters expose high-permission capabilities, make native-enforcement limits explicit, and link installer enforcement evidence when available. + +## Summary + +- OK: `True` +- Targets probed: `4` +- Passed: `4` +- Failed: `0` +- Native enforcement targets: `0` +- Explicit metadata fallbacks: `4` +- Installer enforcement source: `present` +- Installer-enforced targets: `4` +- Installer permission failures: `0` +- World-class native evidence ready: `False` +- Required capabilities: `file_write, network, subprocess` + +| Target | Status | Assurance | Native Enforcement | Metadata Fallback | Installer Enforcement | Residual Risk | +| --- | --- | --- | --- | --- | --- | --- | +| `openai` | `pass` | `metadata-fallback-explicit` | `False` | `True` | `pass` | Client-native permission enforcement is not provided by this target; installer or operator must honor metadata. | +| `claude` | `pass` | `metadata-fallback-explicit` | `False` | `True` | `pass` | Client-native permission enforcement is not provided by this target; installer or operator must honor metadata. | +| `generic` | `pass` | `metadata-fallback-explicit` | `False` | `True` | `pass` | Client-native permission enforcement is not provided by this target; installer or operator must honor metadata. | +| `vscode` | `pass` | `metadata-fallback-explicit` | `False` | `True` | `pass` | Client-native permission enforcement is not provided by this target; installer or operator must honor metadata. | + +## Installer Enforcement + +- Source: `reports/install_simulation.json` +- Source status: `present` +- Package dir matches probe: `True` + +Installer enforcement means the package install simulation blocks missing capability approvals or target enforcement notes. It is supporting local distribution evidence, not proof of target-client native enforcement. + +## Failures + +- None + +## Reviewer Note + +A passing probe means the target contract is explicit and auditable. It does not claim that a host client enforces permissions natively. diff --git a/reports/security_trust_report.json b/reports/security_trust_report.json new file mode 100644 index 0000000..f5cc04b --- /dev/null +++ b/reports/security_trust_report.json @@ -0,0 +1,3588 @@ +{ + "ok": true, + "skill_dir": ".", + "summary": { + "scanned_files": 247, + "script_count": 155, + "internal_module_count": 69, + "secret_findings": 0, + "dependency_files": [ + "requirements-ci.txt" + ], + "network_script_count": 3, + "network_policy_covered_count": 3, + "network_policy_missing_count": 0, + "file_write_script_count": 74, + "permission_required_count": 3, + "permission_approved_count": 3, + "permission_missing_count": 0, + "permission_invalid_count": 0, + "permission_expired_count": 0, + "help_smoke_checked_count": 86, + "help_smoke_failed_count": 0, + "interactive_script_count": 0, + "package_hash_scope": "source-contract-without-generated-reports", + "package_hash_file_count": 247, + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + }, + "failures": [], + "warnings": [], + "secrets": [], + "scripts": [ + { + "path": "scripts/adaptation_patch_safety.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared adaptation patch target parsing and target-file baseline verification helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/adaptation_report_contracts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared adaptation approval and regression report contract decoration helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/adjudicate_output_review.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/apply_adaptation.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Approval-gated adaptive patch application with dry-run, allowlist, regression, and rollback evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/benchmark_release_lock.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_benchmark_reproducibility.py to keep release-lock git status classification out of the report renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_confusion_matrix.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_skill_atlas.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_skill_atlas_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by build_skill_atlas.py to render the static Skill Atlas HTML report.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/build_skill_atlas_opportunities.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by build_skill_atlas.py to keep no-route opportunity detection out of the atlas CLI.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/check_update.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": true, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [ + "https://raw.githubusercontent.com/yaojingang/yao-meta-skill/main/VERSION", + "https://raw.githubusercontent.com/yaojingang/yao-meta-skill/main/manifest.json" + ], + "network_hosts": [ + "raw.githubusercontent.com" + ] + }, + { + "path": "scripts/ci_test.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/collect_feedback.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/compile_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/compile_skill_targets.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by compile_skill for target platform model and contract builders.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/context_sizer.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/create_iteration_snapshot.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/cross_packager.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/cross_packager_contracts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by cross_packager.py to keep platform contract data separate from packaging flow.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/description_optimizer_reporting.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by optimize_description.py to render description optimization reports.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/diff_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/emit_telemetry_event.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_artifact_roles.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to compare preflight and Review Studio artifact-role contracts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_core.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py for shared report loading, comparison, and Markdown rendering helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_phase_queue.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to prevent preflight and Review Studio phase-queue drift.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_release.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to verify release evidence refresh instructions.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_skill_os2_review.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to keep Skill OS 2.0 review summary drift checks out of the main consistency renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/evidence_consistency_world_class.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_evidence_consistency.py to isolate world-class evidence workflow consistency checks.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/export_skill_ir.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/github_benchmark_scan.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": true, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [ + "https://api.github.com" + ], + "network_hosts": [ + "api.github.com" + ] + }, + { + "path": "scripts/governance_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/html_rendering.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Used by report renderers to escape HTML while preserving meaningful falsey values.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/import_output_review_decisions.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Imports human blind A/B reviewer decisions into the canonical output-review decision file.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/import_telemetry_events.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/init_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/judge_blind_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/lint_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/local_output_eval_runner.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/optimize_description.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/output_review_privacy.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by output-review import and world-class human evidence validators to reject raw content, credential, secret, token, and answer-key fields recursively.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/prepare_output_review_kit.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/prepare_world_class_submission_kit.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Prepares editable world-class evidence intake packets without counting drafts as accepted evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/probe_runtime_permissions.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/promotion_checker.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/propose_adaptation.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Turns redacted repeated preference patterns into proposal-only adaptation plans.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/provider_output_eval_runner.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": true, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [ + "https://api.openai.com/v1/responses", + "https://api.deepseek.com/chat/completions" + ], + "network_hosts": [ + "api.deepseek.com", + "api.openai.com" + ] + }, + { + "path": "scripts/python_compat_check.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Checks repository Python source for syntax that can pass locally but fail on the supported CI interpreter.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/reference_synthesis_markdown.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_reference_synthesis.py to keep reference synthesis modeling separate from Markdown rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/registry_audit.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_adoption_drift_report.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_architecture_maintainability.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_artifact_design_profile.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_baseline_compare.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a release-facing benchmark reproducibility manifest and Markdown report.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_context_reports.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_daily_skillops_report.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a Daily SkillOps report that summarizes explicit-source patterns, proposals, approval state, and release evidence without scanning private logs or applying patches.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_description_drift_history.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_eval_dashboard.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_evidence_consistency.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a cross-report evidence consistency gate for generated Skill OS reports.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_intent_confidence.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_intent_dialogue.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_iteration_directions.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_iteration_ledger.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_output_risk_profile.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_portability_report.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_prompt_quality_profile.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_reference_scan.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_reference_synthesis.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_regression_history.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_annotations.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_studio.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_viewer.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_review_waivers.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_interpretation.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders the first-class skill interpretation report while reusing the Skill Overview v2 model and layout.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_os2_audit.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_os2_coverage.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a Skill OS 2.0 blueprint-to-evidence coverage audit.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_skill_overview.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_social_preview.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [ + "http://www.w3.org/2000/svg" + ], + "network_hosts": [ + "www.w3.org" + ] + }, + { + "path": "scripts/render_system_model.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_telemetry_hook_recipes.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_weekly_curator_report.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a weekly SkillOps curator report from generated reports without scanning private logs or writing source files.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_claim_guard.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Scans public claim surfaces so world-class completion is not claimed before accepted evidence exists.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_evidence_intake.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Validates world-class human and external evidence intake packets before ledger review.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_evidence_ledger.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a machine-checkable ledger for world-class external and human evidence gaps.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_evidence_plan.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders an operator runbook for collecting pending world-class evidence without accepting evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_preflight.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a preflight checklist for collecting pending world-class evidence without accepting evidence.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/render_world_class_submission_review.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Renders a read-only review queue for world-class evidence submissions before ledger acceptance.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/resource_boundary_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_action_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by review_studio_actions.py to keep world-class evidence action cards out of generic Review Studio action wiring.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_actions.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep Review Studio action guidance and source refs out of HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_data.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to load Review Studio source reports and metric cards.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_formatting.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to format report dictionaries as audit UI panels.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_gate_contract.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by review_studio_gates.py for stable gate scoring and contract checks.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_gate_helpers.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by review_studio_gates.py to keep reusable Review Studio gate helpers out of the main gate sequence.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_gates.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep Review Studio gate evaluation separate from HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep Review Studio layout and CSS out of gate logic.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_output_review.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to render output review checklist cards.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_panels.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep small Review Studio panel renderers out of the main page composer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_skillops.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep SkillOps summary panels out of the main HTML renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_waivers.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to keep waiver candidate layout out of the main renderer.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_studio_world_class.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_studio.py to render world-class evidence cards.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/review_viewer_data.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_review_viewer.py to assemble Review Viewer data before HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_conformance_suite.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_description_optimization_suite.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_eval_suite.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_output_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/run_output_execution.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/simulate_install.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_ir_paths.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by compiler, registry, conformance, and report scripts to locate canonical Skill IR artifacts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_os2_coverage_markdown.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_os2_coverage.py to keep coverage data assembly separate from Markdown rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_charts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to render inline SVG report charts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_i18n.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to keep bilingual report copy and fallback rules out of HTML rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to keep overview report layout and CSS out of data rendering.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_metrics.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to calculate overview report metrics.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_model.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_skill_overview.py to build the v2 report data model.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_sections.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to assemble overview report quality, risk, and asset sections.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_sources.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to load source files and scan overview package assets.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skill_report_world_class.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by skill_report_model.py to summarize world-class evidence readiness and roadmap actions.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/skillops_opportunity.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_daily_skillops_report.py to score SkillOps opportunities without writing files.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/summarize_user_signals.py", + "interface": "cli", + "interface_declared": true, + "interface_reason": "Scans an explicit local source file and summarizes redacted repeated user preference signals.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/sync_local_install.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/telemetry_native_host.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/trigger_eval.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/trust_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/trust_check_scripts.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Static script inventory helpers imported by trust_check.py.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/upgrade_check.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/validate_skill.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/verify_package.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_evidence_contract.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world-class evidence reports to share intake validation and artifact integrity checks.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_human_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate blind A/B human adjudication evidence from decision rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_native_permission_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate runtime permission evidence from target-level probe rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_native_telemetry_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate native client telemetry evidence from metadata event rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_operator_runbook_coordination.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Builds world-class operator coordination steps and release gates for render_world_class_operator_runbook.py.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_phase_queue.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by prepare_world_class_submission_kit.py to group repair rows into an operator execution queue.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_preflight_layout.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_world_class_preflight.py to keep preflight HTML layout out of data assembly.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_preflight_markdown.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by render_world_class_preflight.py to keep Markdown layout separate from preflight data assembly.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_provider_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world_class_evidence_contract.py to validate provider-backed holdout execution evidence from run rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_repair_checklist.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared by submission kit generation to turn readiness blockers into actionable repair rows.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_source_checks.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by world-class evidence reports to keep source-evidence readiness checks consistent.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_submission_kit_rendering.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared renderer for world-class submission kit Markdown and HTML artifacts.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/world_class_submission_matrix.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Shared by submission kit rendering to summarize draft, artifact, and source-check readiness.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao.py", + "interface": "cli", + "interface_declared": false, + "interface_reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules.", + "has_argparse": true, + "has_main_guard": true, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_adaptation_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep adaptive scan/proposal/apply command handlers outside the thin CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_config.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py for CLI target maps and side-effect-free shaping helpers.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_create_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep skill creation and quickstart command handlers out of the CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_distribution_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep distribution and runtime gate handlers outside the thin CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_operator_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py for operator-facing install, localized docs, and PR review diagnostics.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": true, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_output_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep output evaluation and review handlers outside the thin CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep CLI parser declarations separate from command orchestration.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser_evidence.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao_cli_parser.py to keep evidence command declarations out of the main parser module.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser_operations.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao_cli_parser.py to keep SkillOps, telemetry, and review-loop command declarations out of the main parser module.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_parser_operator.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao_cli_parser.py to keep operator UX command declarations out of the main parser module.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_report_commands.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to keep report and evidence command handlers out of the CLI orchestrator.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_runtime.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py and command modules for shared subprocess execution and JSON payload parsing.", + "has_argparse": false, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": true, + "network_urls": [], + "network_hosts": [] + }, + { + "path": "scripts/yao_cli_telemetry.py", + "interface": "internal-module", + "interface_declared": true, + "interface_reason": "Imported by yao.py to record opt-in metadata-only CLI run telemetry.", + "has_argparse": true, + "has_main_guard": false, + "uses_input": false, + "uses_network": false, + "uses_file_write": false, + "uses_subprocess": false, + "network_urls": [], + "network_hosts": [] + } + ], + "dependencies": { + "present": [ + "requirements-ci.txt" + ], + "pinned": [ + "PyYAML==6.0.3" + ], + "unpinned": [] + }, + "network_policy": { + "present": true, + "path": "security/network_policy.json", + "schema_version": "1.0", + "network_script_count": 3, + "covered_scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "missing_scripts": [], + "mismatches": [], + "default_policy": { + "require_https": true, + "custom_hosts": "deny-by-default", + "review_rule": "Network-capable scripts must declare allowed_hosts and justify any explicit custom-host override." + } + }, + "help_smoke": { + "enabled": true, + "timeout_seconds": 5.0, + "candidate_count": 86, + "checked_count": 86, + "passed_count": 86, + "failed_count": 0, + "skipped_count": 69, + "failed_scripts": [], + "results": [ + { + "path": "scripts/adjudicate_output_review.py", + "command": "python3 scripts/adjudicate_output_review.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: adjudicate_output_review.py [-h] [--blind-pack BLIND_PACK]\n [--answer-key ANSWER_KEY]\n [--decisions DECISIONS]\n [--output-json OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/apply_adaptation.py", + "command": "python3 scripts/apply_adaptation.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: apply_adaptation.py [-h] [--proposal-id PROPOSAL_ID]\n [--patch-file PATCH_FILE]\n [--proposals-json PROPOSALS_JSON]\n [--approval-ledger APPROVAL_LEDGER]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/build_confusion_matrix.py", + "command": "python3 scripts/build_confusion_matrix.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: build_confusion_matrix.py [-h] [--cases CASES]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--history-snapshot-output HISTORY_", + "stderr_excerpt": "" + }, + { + "path": "scripts/build_skill_atlas.py", + "command": "python3 scripts/build_skill_atlas.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: build_skill_atlas.py [-h] [--workspace-root WORKSPACE_ROOT]\n [--output-dir OUTPUT_DIR]\n [--report-html REPORT_HTML]\n [--report-json REPORT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/check_update.py", + "command": "python3 scripts/check_update.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: check_update.py [-h] [--root ROOT] [--cache-path CACHE_PATH]\n [--version-url VERSION_URL]\n [--manifest-url MANIFEST_URL] [--timeout TIMEOUT]\n [--max-age-days MAX_AGE_D", + "stderr_excerpt": "" + }, + { + "path": "scripts/ci_test.py", + "command": "python3 scripts/ci_test.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: ci_test.py [-h] [--tail-lines TAIL_LINES] [targets ...]\n\nRun CI test targets with compact logging.\n\npositional arguments:\n targets Optional target override.\n\noptions:\n -h, --help show this help message and ", + "stderr_excerpt": "" + }, + { + "path": "scripts/collect_feedback.py", + "command": "python3 scripts/collect_feedback.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: collect_feedback.py [-h] [--note NOTE] [--rating RATING]\n [--category CATEGORY]\n [--recommended-action RECOMMENDED_ACTION]\n [--output-json OUTPUT_JSON] [--", + "stderr_excerpt": "" + }, + { + "path": "scripts/compile_skill.py", + "command": "python3 scripts/compile_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: compile_skill.py [-h] [--target TARGET] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD] [--generated-at GENERATED_AT]\n [skill_dir]\n\nCompile Skill IR into target-specific semantic con", + "stderr_excerpt": "" + }, + { + "path": "scripts/context_sizer.py", + "command": "python3 scripts/context_sizer.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: context_sizer.py [-h] [--json] skill_dir\n\nEstimate context size for a skill package.\n\npositional arguments:\n skill_dir Path to the skill directory\n\noptions:\n -h, --help show this help message and exit\n --json Emit machine-", + "stderr_excerpt": "" + }, + { + "path": "scripts/create_iteration_snapshot.py", + "command": "python3 scripts/create_iteration_snapshot.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: create_iteration_snapshot.py [-h] --target TARGET [--label LABEL]\n [--promotion-decisions PROMOTION_DECISIONS]\n [--route-scorecard ROUTE_SCORECARD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/cross_packager.py", + "command": "python3 scripts/cross_packager.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: cross_packager.py [-h] [--platform PLATFORM] [--output-dir OUTPUT_DIR]\n [--expectations EXPECTATIONS] [--zip]\n skill_dir\n\nGenerate lightweight cross-platform packaging artifacts.\n\nposit", + "stderr_excerpt": "" + }, + { + "path": "scripts/diff_eval.py", + "command": "python3 scripts/diff_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: diff_eval.py [-h] before after\n\nDiff two eval reports.\n\npositional arguments:\n before\n after\n\noptions:\n -h, --help show this help message and exit", + "stderr_excerpt": "" + }, + { + "path": "scripts/emit_telemetry_event.py", + "command": "python3 scripts/emit_telemetry_event.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: emit_telemetry_event.py [-h] [--output-jsonl OUTPUT_JSONL]\n [--event {review_event,script_run,skill_activation,skill_output}]\n [--activation-type {explicit,implicit,manual,u", + "stderr_excerpt": "" + }, + { + "path": "scripts/export_skill_ir.py", + "command": "python3 scripts/export_skill_ir.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: export_skill_ir.py [-h] [--output-json OUTPUT_JSON] [--validate-only]\n [skill_dir]\n\nExport a platform-neutral Skill IR document from a skill package.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help ", + "stderr_excerpt": "" + }, + { + "path": "scripts/github_benchmark_scan.py", + "command": "python3 scripts/github_benchmark_scan.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: github_benchmark_scan.py [-h] --query QUERY [--top-n TOP_N]\n [--fixture-dir FIXTURE_DIR]\n [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSO", + "stderr_excerpt": "" + }, + { + "path": "scripts/governance_check.py", + "command": "python3 scripts/governance_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: governance_check.py [-h] [--require-manifest] skill_dir\n\nCheck skill governance metadata and lifecycle readiness.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help show this help message and exit\n --require-manifest", + "stderr_excerpt": "" + }, + { + "path": "scripts/import_output_review_decisions.py", + "command": "python3 scripts/import_output_review_decisions.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: import_output_review_decisions.py [-h] --input INPUT\n [--format {auto,json,jsonl,csv}]\n [--blind-pack BLIND_PACK]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/import_telemetry_events.py", + "command": "python3 scripts/import_telemetry_events.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: import_telemetry_events.py [-h] --input-jsonl INPUT_JSONL\n [--events-jsonl EVENTS_JSONL]\n [--output-json OUTPUT_JSON]\n [--output-md OU", + "stderr_excerpt": "" + }, + { + "path": "scripts/init_skill.py", + "command": "python3 scripts/init_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: init_skill.py [-h] [--description DESCRIPTION] [--title TITLE]\n [--output-dir OUTPUT_DIR]\n [--mode {governed,library,production,scaffold}]\n [--archetype {governed,library,pr", + "stderr_excerpt": "" + }, + { + "path": "scripts/judge_blind_eval.py", + "command": "python3 scripts/judge_blind_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: judge_blind_eval.py [-h] [--description DESCRIPTION]\n [--description-file DESCRIPTION_FILE] --cases CASES\n [--semantic-config SEMANTIC_CONFIG]\n\nRun a rubric-based blind trigger judg", + "stderr_excerpt": "" + }, + { + "path": "scripts/lint_skill.py", + "command": "python3 scripts/lint_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: lint_skill.py [-h] skill_dir\n\nLint a skill package for basic structure issues.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help show this help message and exit", + "stderr_excerpt": "" + }, + { + "path": "scripts/local_output_eval_runner.py", + "command": "python3 scripts/local_output_eval_runner.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: local_output_eval_runner.py [-h] [--execution-kind {command,model}]\n [--provider PROVIDER] [--model MODEL]\n\nLocal deterministic output-eval runner. It executes the runner contract\nwithout claiming pr", + "stderr_excerpt": "" + }, + { + "path": "scripts/optimize_description.py", + "command": "python3 scripts/optimize_description.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: optimize_description.py [-h] --description-file DESCRIPTION_FILE\n [--baseline-description-file BASELINE_DESCRIPTION_FILE]\n --dev-cases DEV_CASES\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/prepare_output_review_kit.py", + "command": "python3 scripts/prepare_output_review_kit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: prepare_output_review_kit.py [-h] [--blind-pack-json BLIND_PACK_JSON]\n [--blind-pack-md BLIND_PACK_MD]\n [--decisions DECISIONS]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/prepare_world_class_submission_kit.py", + "command": "python3 scripts/prepare_world_class_submission_kit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: prepare_world_class_submission_kit.py [-h] [--output-dir OUTPUT_DIR]\n [--evidence-key EVIDENCE_KEY]\n [--overwrite]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/probe_runtime_permissions.py", + "command": "python3 scripts/probe_runtime_permissions.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: probe_runtime_permissions.py [-h] [--package-dir PACKAGE_DIR]\n [--target {openai,claude,generic,vscode}]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/promotion_checker.py", + "command": "python3 scripts/promotion_checker.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: promotion_checker.py [-h] [--optimization-suite OPTIMIZATION_SUITE]\n [--route-scorecard ROUTE_SCORECARD]\n [--output-json OUTPUT_JSON]\n [--output-md OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/propose_adaptation.py", + "command": "python3 scripts/propose_adaptation.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: propose_adaptation.py [-h] [--patterns-json PATTERNS_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/provider_output_eval_runner.py", + "command": "python3 scripts/provider_output_eval_runner.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: provider_output_eval_runner.py [-h] [--provider PROVIDER]\n [--base-url BASE_URL]\n [--api-format {responses,chat-completions}]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/python_compat_check.py", + "command": "python3 scripts/python_compat_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: python_compat_check.py [-h] [--path PATH]\n [--target-python TARGET_PYTHON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/registry_audit.py", + "command": "python3 scripts/registry_audit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: registry_audit.py [-h] [--registry-dir REGISTRY_DIR]\n [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [skill_dir]\n\nBuild and ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_adoption_drift_report.py", + "command": "python3 scripts/render_adoption_drift_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_adoption_drift_report.py [-h] [--events-jsonl EVENTS_JSONL]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_architecture_maintainability.py", + "command": "python3 scripts/render_architecture_maintainability.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_architecture_maintainability.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--warn-lines WARN_LINES]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_artifact_design_profile.py", + "command": "python3 scripts/render_artifact_design_profile.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_artifact_design_profile.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender artifact design direction and visual qua", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_baseline_compare.py", + "command": "python3 scripts/render_baseline_compare.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_baseline_compare.py [-h] --entry ENTRY\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n\nRender a baseline comparison report from description optimization ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_benchmark_reproducibility.py", + "command": "python3 scripts/render_benchmark_reproducibility.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_benchmark_reproducibility.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_context_reports.py", + "command": "python3 scripts/render_context_reports.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_context_reports.py [-h] [--generated-at GENERATED_AT]\n\nRender context budget reports for root and example skills.\n\noptions:\n -h, --help show this help message and exit\n --generated-at GENERATED_AT\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_daily_skillops_report.py", + "command": "python3 scripts/render_daily_skillops_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_daily_skillops_report.py [-h] [--source SOURCE]\n [--patterns-json PATTERNS_JSON]\n [--proposals-json PROPOSALS_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_description_drift_history.py", + "command": "python3 scripts/render_description_drift_history.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_description_drift_history.py [-h] [--history-dir HISTORY_DIR]\n [--output OUTPUT]\n\nRender description optimization drift history.\n\noptions:\n -h, --help show this help messag", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_eval_dashboard.py", + "command": "python3 scripts/render_eval_dashboard.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_eval_dashboard.py [-h] [--eval-dir EVAL_DIR]\n [--description-file DESCRIPTION_FILE]\n [--baseline-description-file BASELINE_DESCRIPTION_FILE]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_evidence_consistency.py", + "command": "python3 scripts/render_evidence_consistency.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_evidence_consistency.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_intent_confidence.py", + "command": "python3 scripts/render_intent_confidence.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_intent_confidence.py [-h] [--context-json CONTEXT_JSON]\n [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\n", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_intent_dialogue.py", + "command": "python3 scripts/render_intent_dialogue.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_intent_dialogue.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender an intent dialogue guide for a skill package.\n\npositional argume", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_iteration_directions.py", + "command": "python3 scripts/render_iteration_directions.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_iteration_directions.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender the top three next iteration directions for a ski", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_iteration_ledger.py", + "command": "python3 scripts/render_iteration_ledger.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_iteration_ledger.py [-h] [--history-dir HISTORY_DIR]\n [--description-history-dir DESCRIPTION_HISTORY_DIR]\n [--route-scorecard ROUTE_SCORECARD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_output_risk_profile.py", + "command": "python3 scripts/render_output_risk_profile.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_output_risk_profile.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender predicted output failure modes and quality constrain", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_portability_report.py", + "command": "python3 scripts/render_portability_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_portability_report.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [skill_dir]\n\nRender a portability score from neutral metadata, contracts, a", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_prompt_quality_profile.py", + "command": "python3 scripts/render_prompt_quality_profile.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_prompt_quality_profile.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender prompt behavior quality, RTF mapping, and n", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_reference_scan.py", + "command": "python3 scripts/render_reference_scan.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_reference_scan.py [-h] [--external-reference EXTERNAL_REFERENCE]\n [--user-reference USER_REFERENCE]\n [--local-constraint LOCAL_CONSTRAINT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_reference_synthesis.py", + "command": "python3 scripts/render_reference_synthesis.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_reference_synthesis.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender a multi-source reference synthesis report for a skil", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_regression_history.py", + "command": "python3 scripts/render_regression_history.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_regression_history.py [-h] [--history-dir HISTORY_DIR]\n [--output OUTPUT]\n\nRender regression history from milestone snapshots.\n\noptions:\n -h, --help show this help message and exi", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_annotations.py", + "command": "python3 scripts/render_review_annotations.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_annotations.py [-h] [--annotations-json ANNOTATIONS_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD] [--write-template]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_studio.py", + "command": "python3 scripts/render_review_studio.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_studio.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender Review Studio 2.0 for a skill package.\n\npositional arguments:\n sk", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_viewer.py", + "command": "python3 scripts/render_review_viewer.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_viewer.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender a compact HTML review viewer for a skill package.\n\npositional argu", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_review_waivers.py", + "command": "python3 scripts/render_review_waivers.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_review_waivers.py [-h] [--waivers-json WAIVERS_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_interpretation.py", + "command": "python3 scripts/render_skill_interpretation.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_interpretation.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender the first-class HTML skill interpretation rep", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_os2_audit.py", + "command": "python3 scripts/render_skill_os2_audit.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_os2_audit.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [skill_dir]\n\nRender a", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_os2_coverage.py", + "command": "python3 scripts/render_skill_os2_coverage.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_os2_coverage.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n [skill_di", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_skill_overview.py", + "command": "python3 scripts/render_skill_overview.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_skill_overview.py [-h] [--output-html OUTPUT_HTML]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender the HTML skill report for a skill package.\n\npositional argument", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_social_preview.py", + "command": "python3 scripts/render_social_preview.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_social_preview.py [-h] [--format {all,png,svg}]\n\nRender the repository social preview image assets.\n\noptions:\n -h, --help show this help message and exit\n --format {all,png,svg}\n Asset forma", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_system_model.py", + "command": "python3 scripts/render_system_model.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_system_model.py [-h] [--output-md OUTPUT_MD]\n [--output-json OUTPUT_JSON]\n [skill_dir]\n\nRender a systems-thinking model for a skill package.\n\npositional arguments:\n sk", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_telemetry_hook_recipes.py", + "command": "python3 scripts/render_telemetry_hook_recipes.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_telemetry_hook_recipes.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--output-jsonl OUTPUT_JSONL]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_weekly_curator_report.py", + "command": "python3 scripts/render_weekly_curator_report.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_weekly_curator_report.py [-h] [--daily-json DAILY_JSON]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_claim_guard.py", + "command": "python3 scripts/render_world_class_claim_guard.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_claim_guard.py [-h] [--claim-surface CLAIM_SURFACE]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_evidence_intake.py", + "command": "python3 scripts/render_world_class_evidence_intake.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_evidence_intake.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_evidence_ledger.py", + "command": "python3 scripts/render_world_class_evidence_ledger.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_evidence_ledger.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--submissions-dir SUBMISSIONS_DIR]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_evidence_plan.py", + "command": "python3 scripts/render_world_class_evidence_plan.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_evidence_plan.py [-h] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--generated-at GENERATED_AT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_operator_runbook.py", + "command": "python3 scripts/render_world_class_operator_runbook.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_operator_runbook.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_preflight.py", + "command": "python3 scripts/render_world_class_preflight.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_preflight.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n [--output-md OU", + "stderr_excerpt": "" + }, + { + "path": "scripts/render_world_class_submission_review.py", + "command": "python3 scripts/render_world_class_submission_review.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: render_world_class_submission_review.py [-h]\n [--submissions-dir SUBMISSIONS_DIR]\n [--output-json OUTPUT_JSON]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/resource_boundary_check.py", + "command": "python3 scripts/resource_boundary_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: resource_boundary_check.py [-h]\n [--max-initial-tokens MAX_INITIAL_TOKENS]\n [--warn-skill-body-tokens WARN_SKILL_BODY_TOKENS]\n skill_d", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_conformance_suite.py", + "command": "python3 scripts/run_conformance_suite.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_conformance_suite.py [-h]\n [--target {openai,claude,agent-skills,vscode,generic}]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_description_optimization_suite.py", + "command": "python3 scripts/run_description_optimization_suite.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_description_optimization_suite.py [-h]\n [--history-snapshot-output HISTORY_SNAPSHOT_OUTPUT]\n [--snapshot-date SNAPSHOT_DATE]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_eval_suite.py", + "command": "python3 scripts/run_eval_suite.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_eval_suite.py [-h] [--eval-dir EVAL_DIR]\n [--description-file DESCRIPTION_FILE]\n [--baseline-description-file BASELINE_DESCRIPTION_FILE]\n [--output-file OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_output_eval.py", + "command": "python3 scripts/run_output_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_output_eval.py [-h] [--cases CASES] [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--blind-pack-json BLIND_PACK_JSON]\n [--blind-pack-md BLIND_PACK", + "stderr_excerpt": "" + }, + { + "path": "scripts/run_output_execution.py", + "command": "python3 scripts/run_output_execution.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: run_output_execution.py [-h] [--cases CASES]\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--runner-command RUNNER_COMMAND]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/simulate_install.py", + "command": "python3 scripts/simulate_install.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: simulate_install.py [-h] [--package-dir PACKAGE_DIR]\n [--install-root INSTALL_ROOT]\n [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n [--generated-at G", + "stderr_excerpt": "" + }, + { + "path": "scripts/summarize_user_signals.py", + "command": "python3 scripts/summarize_user_signals.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: summarize_user_signals.py [-h] --source SOURCE\n [--output-json OUTPUT_JSON]\n [--output-md OUTPUT_MD]\n [--min-support MIN_SUPPORT]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/sync_local_install.py", + "command": "python3 scripts/sync_local_install.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: sync_local_install.py [-h] [--root ROOT] [--install-dir INSTALL_DIR]\n [--package-dir PACKAGE_DIR]\n [--generated-at GENERATED_AT]\n [--skip-install-pre", + "stderr_excerpt": "" + }, + { + "path": "scripts/telemetry_native_host.py", + "command": "python3 scripts/telemetry_native_host.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: telemetry_native_host.py [-h] [--output-jsonl OUTPUT_JSONL]\n [--message-json MESSAGE_JSON] [--stdio]\n [--dry-run] [--write-manifest WRITE_MANIFEST]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/trigger_eval.py", + "command": "python3 scripts/trigger_eval.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: trigger_eval.py [-h] [--description DESCRIPTION]\n [--description-file DESCRIPTION_FILE]\n [--baseline-description BASELINE_DESCRIPTION]\n [--baseline-description-file BA", + "stderr_excerpt": "" + }, + { + "path": "scripts/trust_check.py", + "command": "python3 scripts/trust_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: trust_check.py [-h] [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n [--skip-help-smoke]\n [--help-smoke-timeout HELP_SMOKE_TIMEOUT]\n [skill_dir]\n\nRun Skill OS trust a", + "stderr_excerpt": "" + }, + { + "path": "scripts/upgrade_check.py", + "command": "python3 scripts/upgrade_check.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: upgrade_check.py [-h] --previous-package-json PREVIOUS_PACKAGE_JSON\n [--current-package-json CURRENT_PACKAGE_JSON]\n [--output-json OUTPUT_JSON] [--output-md OUTPUT_MD]\n ", + "stderr_excerpt": "" + }, + { + "path": "scripts/validate_skill.py", + "command": "python3 scripts/validate_skill.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: validate_skill.py [-h] skill_dir\n\nValidate a skill package.\n\npositional arguments:\n skill_dir\n\noptions:\n -h, --help show this help message and exit", + "stderr_excerpt": "" + }, + { + "path": "scripts/verify_package.py", + "command": "python3 scripts/verify_package.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: verify_package.py [-h] [--package-dir PACKAGE_DIR]\n [--expectations EXPECTATIONS]\n [--registry-json REGISTRY_JSON]\n [--output-json OUTPUT_JSON] [--output-md OUTP", + "stderr_excerpt": "" + }, + { + "path": "scripts/yao.py", + "command": "python3 scripts/yao.py --help", + "returncode": 0, + "timed_out": false, + "passed": true, + "has_help_text": true, + "stdout_excerpt": "usage: yao.py [-h] [--record-cli-telemetry] [--no-cli-telemetry]\n [--telemetry-events-jsonl TELEMETRY_EVENTS_JSONL]\n {init,quickstart,validate,optimize-description,promote-check,python-compat,architecture-audit,rev", + "stderr_excerpt": "" + } + ], + "skipped": [ + { + "path": "scripts/adaptation_patch_safety.py", + "reason": "internal module" + }, + { + "path": "scripts/adaptation_report_contracts.py", + "reason": "internal module" + }, + { + "path": "scripts/benchmark_release_lock.py", + "reason": "internal module" + }, + { + "path": "scripts/build_skill_atlas_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/build_skill_atlas_opportunities.py", + "reason": "internal module" + }, + { + "path": "scripts/compile_skill_targets.py", + "reason": "internal module" + }, + { + "path": "scripts/cross_packager_contracts.py", + "reason": "internal module" + }, + { + "path": "scripts/description_optimizer_reporting.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_artifact_roles.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_core.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_phase_queue.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_release.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_skill_os2_review.py", + "reason": "internal module" + }, + { + "path": "scripts/evidence_consistency_world_class.py", + "reason": "internal module" + }, + { + "path": "scripts/html_rendering.py", + "reason": "internal module" + }, + { + "path": "scripts/output_review_privacy.py", + "reason": "internal module" + }, + { + "path": "scripts/reference_synthesis_markdown.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_action_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_actions.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_data.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_formatting.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_gate_contract.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_gate_helpers.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_gates.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_output_review.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_panels.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_skillops.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_waivers.py", + "reason": "internal module" + }, + { + "path": "scripts/review_studio_world_class.py", + "reason": "internal module" + }, + { + "path": "scripts/review_viewer_data.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_ir_paths.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_os2_coverage_markdown.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_charts.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_i18n.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_metrics.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_model.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_sections.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_sources.py", + "reason": "internal module" + }, + { + "path": "scripts/skill_report_world_class.py", + "reason": "internal module" + }, + { + "path": "scripts/skillops_opportunity.py", + "reason": "internal module" + }, + { + "path": "scripts/trust_check_scripts.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_evidence_contract.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_human_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_native_permission_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_native_telemetry_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_operator_runbook_coordination.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_phase_queue.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_preflight_layout.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_preflight_markdown.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_provider_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_repair_checklist.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_source_checks.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_submission_kit_rendering.py", + "reason": "internal module" + }, + { + "path": "scripts/world_class_submission_matrix.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_adaptation_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_config.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_create_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_distribution_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_operator_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_output_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser_evidence.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser_operations.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_parser_operator.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_report_commands.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_runtime.py", + "reason": "internal module" + }, + { + "path": "scripts/yao_cli_telemetry.py", + "reason": "internal module" + } + ] + }, + "permission_governance": { + "present": true, + "path": "security/permission_policy.json", + "schema_version": "1.0", + "reviewed_at": "2026-06-13", + "required_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "approved_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "missing_capabilities": [], + "invalid_capabilities": [], + "expired_capabilities": [], + "approval_count": 3, + "required_count": 3, + "missing_count": 0, + "invalid_count": 0, + "expired_count": 0, + "approvals": [ + { + "capability": "network", + "status": "approved", + "scripts": [ + "scripts/check_update.py", + "scripts/github_benchmark_scan.py", + "scripts/provider_output_eval_runner.py" + ], + "reviewer": "Yao Team", + "scope": "Only scripts declared in security/network_policy.json may perform outbound requests.", + "reason": "Network use is bounded to public GitHub metadata, benchmark discovery, update checks, and reviewed provider output-eval endpoints, with allowed hosts reviewed in security/network_policy.json.", + "expires_at": "2026-09-30", + "evidence": [ + "security/network_policy.json", + "reports/security_trust_report.md" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + }, + "validation": [] + }, + { + "capability": "file_write", + "status": "approved", + "scripts": [ + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/emit_telemetry_event.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/python_compat_check.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trust_check.py", + "scripts/upgrade_check.py", + "scripts/verify_package.py", + "scripts/yao_cli_operator_commands.py" + ], + "reviewer": "Yao Team", + "scope": "Local generated reports, registry metadata, temporary test outputs, and package artifacts under the skill workspace.", + "reason": "File writes are required to create auditable reports, compiled target contracts, package artifacts, and local test fixtures; package safety checks exclude generated temp outputs from archives.", + "expires_at": "2026-09-30", + "evidence": [ + "reports/package_verification.md", + "reports/install_simulation.md", + "security/permission_policy.md" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + }, + "validation": [] + }, + { + "capability": "subprocess", + "status": "approved", + "scripts": [ + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/ci_test.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/run_eval_suite.py", + "scripts/run_output_execution.py", + "scripts/sync_local_install.py", + "scripts/trust_check.py", + "scripts/yao.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_runtime.py" + ], + "reviewer": "Yao Team", + "scope": "Local deterministic validation, packaging, install simulation, and CI orchestration commands.", + "reason": "Subprocess use is limited to local verification and packaging flows that are surfaced through CLI help smoke tests and CI targets.", + "expires_at": "2026-09-30", + "evidence": [ + "reports/security_trust_report.md", + "reports/review-studio.html" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + }, + "validation": [] + } + ] + }, + "trust_metadata": { + "source_tier": "local", + "remote_inline_execution": "forbid", + "remote_metadata_policy": "allow-metadata-only" + }, + "artifacts": { + "json": "reports/security_trust_report.json", + "markdown": "reports/security_trust_report.md" + } +} diff --git a/reports/security_trust_report.md b/reports/security_trust_report.md new file mode 100644 index 0000000..d070a34 --- /dev/null +++ b/reports/security_trust_report.md @@ -0,0 +1,219 @@ +# Security Trust Report + +- OK: `True` +- Scanned files: `247` +- Scripts: `155` +- Internal script modules: `69` +- Secret findings: `0` +- Network-capable scripts: `3` +- Network policy covered scripts: `3` +- Network policy missing scripts: `0` +- File-write scripts: `74` +- Permission approvals: `3 / 3` +- Permission approval gaps: `0` +- CLI help smoke checked: `86` +- CLI help smoke failures: `0` +- Interactive scripts: `0` +- Package hash scope: `source-contract-without-generated-reports` +- Package hash files: `247` +- Package SHA256: `db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c` + +## Failures + +- None + +## Warnings + +- None + +## Dependency Evidence + +- Files: `requirements-ci.txt` +- Pinned entries: `1` +- Unpinned entries: `0` + +## Network Policy + +- Policy file: `security/network_policy.json` +- Present: `True` +- Covered scripts: `3` +- Missing scripts: `none` +- Mismatches: `0` + +## Permission Governance + +- Policy file: `security/permission_policy.json` +- Present: `True` +- Required capabilities: `file_write, network, subprocess` +- Approved capabilities: `file_write, network, subprocess` +- Missing approvals: `none` +- Invalid approvals: `none` +- Expired approvals: `none` + +## CLI Help Smoke + +- Enabled: `True` +- Timeout seconds: `5.0` +- Checked scripts: `86` +- Passed scripts: `86` +- Failed scripts: `none` + +## Script Surface + +| Script | Interface | Declared | Argparse | Main Guard | Input | Network | File Write | Subprocess | Reason | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| scripts/adaptation_patch_safety.py | internal-module | True | False | False | False | False | False | False | Shared adaptation patch target parsing and target-file baseline verification helpers. | +| scripts/adaptation_report_contracts.py | internal-module | True | False | False | False | False | False | False | Shared adaptation approval and regression report contract decoration helpers. | +| scripts/adjudicate_output_review.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/apply_adaptation.py | cli | True | True | True | False | False | True | True | Approval-gated adaptive patch application with dry-run, allowlist, regression, and rollback evidence. | +| scripts/benchmark_release_lock.py | internal-module | True | False | False | False | False | False | True | Imported by render_benchmark_reproducibility.py to keep release-lock git status classification out of the report renderer. | +| scripts/build_confusion_matrix.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/build_skill_atlas.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/build_skill_atlas_layout.py | internal-module | True | False | False | False | False | False | False | Imported by build_skill_atlas.py to render the static Skill Atlas HTML report. | +| scripts/build_skill_atlas_opportunities.py | internal-module | True | False | False | False | False | False | False | Imported by build_skill_atlas.py to keep no-route opportunity detection out of the atlas CLI. | +| scripts/check_update.py | cli | False | True | True | False | True | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/ci_test.py | cli | False | True | True | False | False | True | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/collect_feedback.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/compile_skill.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/compile_skill_targets.py | internal-module | True | False | False | False | False | False | False | Imported by compile_skill for target platform model and contract builders. | +| scripts/context_sizer.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/create_iteration_snapshot.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/cross_packager.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/cross_packager_contracts.py | internal-module | True | False | False | False | False | False | False | Imported by cross_packager.py to keep platform contract data separate from packaging flow. | +| scripts/description_optimizer_reporting.py | internal-module | True | False | False | False | False | False | False | Imported by optimize_description.py to render description optimization reports. | +| scripts/diff_eval.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/emit_telemetry_event.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/evidence_consistency_artifact_roles.py | internal-module | True | False | False | False | False | False | False | Imported by render_evidence_consistency.py to compare preflight and Review Studio artifact-role contracts. | +| scripts/evidence_consistency_core.py | internal-module | True | False | False | False | False | False | False | Imported by render_evidence_consistency.py for shared report loading, comparison, and Markdown rendering helpers. | +| scripts/evidence_consistency_phase_queue.py | internal-module | True | False | False | False | False | False | False | Imported by render_evidence_consistency.py to prevent preflight and Review Studio phase-queue drift. | +| scripts/evidence_consistency_release.py | internal-module | True | False | False | False | False | False | False | Imported by render_evidence_consistency.py to verify release evidence refresh instructions. | +| scripts/evidence_consistency_skill_os2_review.py | internal-module | True | False | False | False | False | False | False | Imported by render_evidence_consistency.py to keep Skill OS 2.0 review summary drift checks out of the main consistency renderer. | +| scripts/evidence_consistency_world_class.py | internal-module | True | False | False | False | False | False | False | Imported by render_evidence_consistency.py to isolate world-class evidence workflow consistency checks. | +| scripts/export_skill_ir.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/github_benchmark_scan.py | cli | False | True | True | False | True | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/governance_check.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/html_rendering.py | internal-module | True | False | False | False | False | False | False | Used by report renderers to escape HTML while preserving meaningful falsey values. | +| scripts/import_output_review_decisions.py | cli | True | True | True | False | False | True | False | Imports human blind A/B reviewer decisions into the canonical output-review decision file. | +| scripts/import_telemetry_events.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/init_skill.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/judge_blind_eval.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/lint_skill.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/local_output_eval_runner.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/optimize_description.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/output_review_privacy.py | internal-module | True | False | False | False | False | False | False | Imported by output-review import and world-class human evidence validators to reject raw content, credential, secret, token, and answer-key fields recursively. | +| scripts/prepare_output_review_kit.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/prepare_world_class_submission_kit.py | cli | True | True | True | False | False | True | False | Prepares editable world-class evidence intake packets without counting drafts as accepted evidence. | +| scripts/probe_runtime_permissions.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/promotion_checker.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/propose_adaptation.py | cli | True | True | True | False | False | True | False | Turns redacted repeated preference patterns into proposal-only adaptation plans. | +| scripts/provider_output_eval_runner.py | cli | False | True | True | False | True | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/python_compat_check.py | cli | True | True | True | False | False | True | False | Checks repository Python source for syntax that can pass locally but fail on the supported CI interpreter. | +| scripts/reference_synthesis_markdown.py | internal-module | True | False | False | False | False | False | False | Imported by render_reference_synthesis.py to keep reference synthesis modeling separate from Markdown rendering. | +| scripts/registry_audit.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_adoption_drift_report.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_architecture_maintainability.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_artifact_design_profile.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_baseline_compare.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_benchmark_reproducibility.py | cli | True | True | True | False | False | True | True | Renders a release-facing benchmark reproducibility manifest and Markdown report. | +| scripts/render_context_reports.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_daily_skillops_report.py | cli | True | True | True | False | False | True | False | Renders a Daily SkillOps report that summarizes explicit-source patterns, proposals, approval state, and release evidence without scanning private logs or applying patches. | +| scripts/render_description_drift_history.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_eval_dashboard.py | cli | False | True | True | False | False | True | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_evidence_consistency.py | cli | True | True | True | False | False | True | True | Renders a cross-report evidence consistency gate for generated Skill OS reports. | +| scripts/render_intent_confidence.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_intent_dialogue.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_iteration_directions.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_iteration_ledger.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_output_risk_profile.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_portability_report.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_prompt_quality_profile.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_reference_scan.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_reference_synthesis.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_regression_history.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_review_annotations.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_review_studio.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_review_viewer.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_review_waivers.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_skill_interpretation.py | cli | True | True | True | False | False | True | False | Renders the first-class skill interpretation report while reusing the Skill Overview v2 model and layout. | +| scripts/render_skill_os2_audit.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_skill_os2_coverage.py | cli | True | True | True | False | False | True | False | Renders a Skill OS 2.0 blueprint-to-evidence coverage audit. | +| scripts/render_skill_overview.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_social_preview.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_system_model.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_telemetry_hook_recipes.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_weekly_curator_report.py | cli | True | True | True | False | False | True | False | Renders a weekly SkillOps curator report from generated reports without scanning private logs or writing source files. | +| scripts/render_world_class_claim_guard.py | cli | True | True | True | False | False | True | False | Scans public claim surfaces so world-class completion is not claimed before accepted evidence exists. | +| scripts/render_world_class_evidence_intake.py | cli | True | True | True | False | False | True | False | Validates world-class human and external evidence intake packets before ledger review. | +| scripts/render_world_class_evidence_ledger.py | cli | True | True | True | False | False | True | False | Renders a machine-checkable ledger for world-class external and human evidence gaps. | +| scripts/render_world_class_evidence_plan.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/render_world_class_operator_runbook.py | cli | True | True | True | False | False | True | False | Renders an operator runbook for collecting pending world-class evidence without accepting evidence. | +| scripts/render_world_class_preflight.py | cli | True | True | True | False | False | True | False | Renders a preflight checklist for collecting pending world-class evidence without accepting evidence. | +| scripts/render_world_class_submission_review.py | cli | True | True | True | False | False | True | False | Renders a read-only review queue for world-class evidence submissions before ledger acceptance. | +| scripts/resource_boundary_check.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/review_studio_action_evidence.py | internal-module | True | False | False | False | False | False | False | Imported by review_studio_actions.py to keep world-class evidence action cards out of generic Review Studio action wiring. | +| scripts/review_studio_actions.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to keep Review Studio action guidance and source refs out of HTML rendering. | +| scripts/review_studio_data.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to load Review Studio source reports and metric cards. | +| scripts/review_studio_formatting.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to format report dictionaries as audit UI panels. | +| scripts/review_studio_gate_contract.py | internal-module | True | False | False | False | False | False | False | Imported by review_studio_gates.py for stable gate scoring and contract checks. | +| scripts/review_studio_gate_helpers.py | internal-module | True | False | False | False | False | False | False | Imported by review_studio_gates.py to keep reusable Review Studio gate helpers out of the main gate sequence. | +| scripts/review_studio_gates.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to keep Review Studio gate evaluation separate from HTML rendering. | +| scripts/review_studio_layout.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to keep Review Studio layout and CSS out of gate logic. | +| scripts/review_studio_output_review.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to render output review checklist cards. | +| scripts/review_studio_panels.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to keep small Review Studio panel renderers out of the main page composer. | +| scripts/review_studio_skillops.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to keep SkillOps summary panels out of the main HTML renderer. | +| scripts/review_studio_waivers.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to keep waiver candidate layout out of the main renderer. | +| scripts/review_studio_world_class.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_studio.py to render world-class evidence cards. | +| scripts/review_viewer_data.py | internal-module | True | False | False | False | False | False | False | Imported by render_review_viewer.py to assemble Review Viewer data before HTML rendering. | +| scripts/run_conformance_suite.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/run_description_optimization_suite.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/run_eval_suite.py | cli | False | True | True | False | False | False | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/run_output_eval.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/run_output_execution.py | cli | False | True | True | False | False | True | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/simulate_install.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/skill_ir_paths.py | internal-module | True | False | False | False | False | False | False | Imported by compiler, registry, conformance, and report scripts to locate canonical Skill IR artifacts. | +| scripts/skill_os2_coverage_markdown.py | internal-module | True | False | False | False | False | False | False | Imported by render_skill_os2_coverage.py to keep coverage data assembly separate from Markdown rendering. | +| scripts/skill_report_charts.py | internal-module | True | False | False | False | False | False | False | Imported by render_skill_overview.py to render inline SVG report charts. | +| scripts/skill_report_i18n.py | internal-module | True | False | False | False | False | False | False | Imported by render_skill_overview.py to keep bilingual report copy and fallback rules out of HTML rendering. | +| scripts/skill_report_layout.py | internal-module | True | False | False | False | False | False | False | Imported by render_skill_overview.py to keep overview report layout and CSS out of data rendering. | +| scripts/skill_report_metrics.py | internal-module | True | False | False | False | False | False | False | Imported by skill_report_model.py to calculate overview report metrics. | +| scripts/skill_report_model.py | internal-module | True | False | False | False | False | False | False | Imported by render_skill_overview.py to build the v2 report data model. | +| scripts/skill_report_sections.py | internal-module | True | False | False | False | False | False | False | Imported by skill_report_model.py to assemble overview report quality, risk, and asset sections. | +| scripts/skill_report_sources.py | internal-module | True | False | False | False | False | False | False | Imported by skill_report_model.py to load source files and scan overview package assets. | +| scripts/skill_report_world_class.py | internal-module | True | False | False | False | False | False | False | Imported by skill_report_model.py to summarize world-class evidence readiness and roadmap actions. | +| scripts/skillops_opportunity.py | internal-module | True | False | False | False | False | False | False | Imported by render_daily_skillops_report.py to score SkillOps opportunities without writing files. | +| scripts/summarize_user_signals.py | cli | True | True | True | False | False | True | False | Scans an explicit local source file and summarizes redacted repeated user preference signals. | +| scripts/sync_local_install.py | cli | False | True | True | False | False | True | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/telemetry_native_host.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/trigger_eval.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/trust_check.py | cli | False | True | True | False | False | True | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/trust_check_scripts.py | internal-module | True | True | True | False | False | False | False | Static script inventory helpers imported by trust_check.py. | +| scripts/upgrade_check.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/validate_skill.py | cli | False | True | True | False | False | False | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/verify_package.py | cli | False | True | True | False | False | True | False | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/world_class_evidence_contract.py | internal-module | True | False | False | False | False | False | False | Imported by world-class evidence reports to share intake validation and artifact integrity checks. | +| scripts/world_class_human_evidence.py | internal-module | True | False | False | False | False | False | False | Imported by world_class_evidence_contract.py to validate blind A/B human adjudication evidence from decision rows. | +| scripts/world_class_native_permission_evidence.py | internal-module | True | False | False | False | False | False | False | Imported by world_class_evidence_contract.py to validate runtime permission evidence from target-level probe rows. | +| scripts/world_class_native_telemetry_evidence.py | internal-module | True | False | False | False | False | False | False | Imported by world_class_evidence_contract.py to validate native client telemetry evidence from metadata event rows. | +| scripts/world_class_operator_runbook_coordination.py | internal-module | True | False | False | False | False | False | False | Builds world-class operator coordination steps and release gates for render_world_class_operator_runbook.py. | +| scripts/world_class_phase_queue.py | internal-module | True | False | False | False | False | False | False | Imported by prepare_world_class_submission_kit.py to group repair rows into an operator execution queue. | +| scripts/world_class_preflight_layout.py | internal-module | True | False | False | False | False | False | False | Imported by render_world_class_preflight.py to keep preflight HTML layout out of data assembly. | +| scripts/world_class_preflight_markdown.py | internal-module | True | False | False | False | False | False | False | Imported by render_world_class_preflight.py to keep Markdown layout separate from preflight data assembly. | +| scripts/world_class_provider_evidence.py | internal-module | True | False | False | False | False | False | False | Imported by world_class_evidence_contract.py to validate provider-backed holdout execution evidence from run rows. | +| scripts/world_class_repair_checklist.py | internal-module | True | False | False | False | False | False | False | Shared by submission kit generation to turn readiness blockers into actionable repair rows. | +| scripts/world_class_source_checks.py | internal-module | True | False | False | False | False | False | False | Imported by world-class evidence reports to keep source-evidence readiness checks consistent. | +| scripts/world_class_submission_kit_rendering.py | internal-module | True | False | False | False | False | False | False | Shared renderer for world-class submission kit Markdown and HTML artifacts. | +| scripts/world_class_submission_matrix.py | internal-module | True | False | False | False | False | False | False | Shared by submission kit rendering to summarize draft, artifact, and source-check readiness. | +| scripts/yao.py | cli | False | True | True | False | False | False | True | Default CLI classification; add SCRIPT_INTERFACE for internal modules. | +| scripts/yao_cli_adaptation_commands.py | internal-module | True | True | False | False | False | False | False | Imported by yao.py to keep adaptive scan/proposal/apply command handlers outside the thin CLI orchestrator. | +| scripts/yao_cli_config.py | internal-module | True | False | False | False | False | False | False | Imported by yao.py for CLI target maps and side-effect-free shaping helpers. | +| scripts/yao_cli_create_commands.py | internal-module | True | True | False | False | False | False | False | Imported by yao.py to keep skill creation and quickstart command handlers out of the CLI orchestrator. | +| scripts/yao_cli_distribution_commands.py | internal-module | True | True | False | False | False | False | False | Imported by yao.py to keep distribution and runtime gate handlers outside the thin CLI orchestrator. | +| scripts/yao_cli_operator_commands.py | internal-module | True | True | False | False | False | True | True | Imported by yao.py for operator-facing install, localized docs, and PR review diagnostics. | +| scripts/yao_cli_output_commands.py | internal-module | True | True | False | False | False | False | False | Imported by yao.py to keep output evaluation and review handlers outside the thin CLI orchestrator. | +| scripts/yao_cli_parser.py | internal-module | True | True | False | False | False | False | False | Imported by yao.py to keep CLI parser declarations separate from command orchestration. | +| scripts/yao_cli_parser_evidence.py | internal-module | True | True | False | False | False | False | False | Imported by yao_cli_parser.py to keep evidence command declarations out of the main parser module. | +| scripts/yao_cli_parser_operations.py | internal-module | True | True | False | False | False | False | False | Imported by yao_cli_parser.py to keep SkillOps, telemetry, and review-loop command declarations out of the main parser module. | +| scripts/yao_cli_parser_operator.py | internal-module | True | True | False | False | False | False | False | Imported by yao_cli_parser.py to keep operator UX command declarations out of the main parser module. | +| scripts/yao_cli_report_commands.py | internal-module | True | True | False | False | False | False | False | Imported by yao.py to keep report and evidence command handlers out of the CLI orchestrator. | +| scripts/yao_cli_runtime.py | internal-module | True | False | False | False | False | False | True | Imported by yao.py and command modules for shared subprocess execution and JSON payload parsing. | +| scripts/yao_cli_telemetry.py | internal-module | True | True | False | False | False | False | False | Imported by yao.py to record opt-in metadata-only CLI run telemetry. | diff --git a/reports/skill-interpretation.html b/reports/skill-interpretation.html new file mode 100644 index 0000000..0956b04 --- /dev/null +++ b/reports/skill-interpretation.html @@ -0,0 +1,1005 @@ + + + + + + yao-meta-skill Skill 生成审计报告 + + + + +
    + + +
    +
    +
    +
    +
    +

    YAO Skill 生成审计报告YAO Skill Generation Audit

    +

    yao-meta-skill

    +

    技能审计报告Yao Meta Skill Audit Report

    +

    这份报告默认使用中文简体,把新 Skill 的定位、指标、原理、契约、质量、风险、资产和迭代路线整理为一份可审计的 HTML 报告。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.

    +
    技能名称:yao-meta-skillSkill name: yao-meta-skill成熟度:治理Maturity: governed格式:Agent SkillsFormat: agent-skills更新时间:2026-03-31Updated: 2026-03-31
    +
    openaiopenaiclaudeclaudegenericgenericvscodevscode
    +
    + +
    +
    完整度Completeness100SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
    触发清晰Trigger clarity100frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
    证据充分Evidence depth100已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.
    上下文成本Context cost42入口约 314 个词/字,references 约 16934 个词/字。Entrypoint is about 314 words/characters; references are about 16934.
    +
    + +
    +
    +
    +

    技能概述Overview

    +

    先用一屏说明这个 Skill 是什么、给谁用、交付什么。A first-screen explanation of what this skill is, who it serves, and what it delivers.

    +
    +
    +
    +

    作用定位Role

    +
    • Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
    • 把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。Turn one-off experience into a reusable, evaluable, and portable skill package.
    • 交付结果:SKILL.md, agents/interface.yaml, skill-ir/examples/yao-meta-skill.json, reports/compiled_targets.md, reports/output_quality_scorecard.md, reports/output_execution_runs.md, reports/output_blind_review_pack.md, reports/output_review_kit.md, reports/output_review_adjudication.md, reports/benchmark_reproducibility.md, reports/output_blind_answer_key.json, reports/conformance_matrix.md, reports/security_trust_report.md, reports/runtime_permission_probes.md, reports/skill_atlas.html, reports/registry_audit.md, reports/package_verification.md, reports/install_simulation.md, reports/upgrade_check.md, reports/adoption_drift_report.md, reports/review_waivers.md, reports/world_class_evidence_plan.md, reports/world_class_evidence_ledger.md, reports/review_annotations.md, reports/review-studio.html, reports/skill-interpretation.html, reports/skill-overview.html, reports/skill-interpretation.jsonDeliverables: SKILL.md, agents/interface.yaml, skill-ir/examples/yao-meta-skill.json, reports/compiled_targets.md, reports/output_quality_scorecard.md, reports/output_execution_runs.md, reports/output_blind_review_pack.md, reports/output_review_kit.md, reports/output_review_adjudication.md, reports/benchmark_reproducibility.md, reports/output_blind_answer_key.json, reports/conformance_matrix.md, reports/security_trust_report.md, reports/runtime_permission_probes.md, reports/skill_atlas.html, reports/registry_audit.md, reports/package_verification.md, reports/install_simulation.md, reports/upgrade_check.md, reports/adoption_drift_report.md, reports/review_waivers.md, reports/world_class_evidence_plan.md, reports/world_class_evidence_ledger.md, reports/review_annotations.md, reports/review-studio.html, reports/skill-interpretation.html, reports/skill-overview.html, reports/skill-interpretation.json
    +
    +
    交付流程Delivery Flow输入材料Input materialSkill 包体Skill package可复用能力Reusable capability
    交付流程把用户输入、生成的包体和可复用能力放在一条线上。The delivery flow places user input, generated package, and reusable capability on one path.
    +
    +
    +
    + +
    +
    +
    +

    总览指标Metrics

    +

    分数来自本地文件和 reports 证据,缺失时明确标为证据不足。Scores are derived from package files and reports; missing inputs are shown as evidence gaps.

    +
    +
    +

    指标判读Reading

    +

    先看雷达图判断能力短板,再看每项分数的证据原因。分数不是装饰数字,必须和本地文件、reports 证据或证据不足提示对应。Read the radar first for weak spots, then inspect each score with its evidence. Scores must map to local files, reports, or explicit evidence gaps.

    +
    +
    +
    +
    +
    +
    评分雷达Rating Radar完整度Completeness触发清晰Trigger clarity证据充分Evidence depth可维护性Maintainability可迁移性Portability
    评分雷达展示结构完整度、触发边界、证据、维护和迁移的相对强弱。The radar chart compares completeness, trigger clarity, evidence, maintainability, and portability.
    +
    +

    成熟度条Maturity Bar

    +

    这里把每个指标压缩为状态、分数和第一条证据,先给出整体判断,再进入下方明细。This compresses each metric into status, score, and the first evidence item before the detailed cards below.

    +
    1. 稳定Stable完整度Completeness100SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
    2. 稳定Stable触发清晰Trigger clarity100frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
    3. 稳定Stable证据充分Evidence depth100已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.
    4. 稳定Stable可维护性Maintainability100SKILL.md 约 314 个词/字。SKILL.md is about 314 words/characters.
    5. 稳定Stable可迁移性Portability100agents/interface.yaml 已存在。agents/interface.yaml exists.
    6. 关注Watch上下文成本Context cost42入口约 314 个词/字,references 约 16934 个词/字。Entrypoint is about 314 words/characters; references are about 16934.
    +
    +
    +
    +
    指标明细Metric Details
    +
    完整度Completeness100
    • SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
    • README.md 已存在,便于人工阅读。README.md exists for human-readable usage.
    • agents/interface.yaml 已存在,便于跨平台适配。agents/interface.yaml exists for cross-platform adaptation.
    触发清晰Trigger clarity100
    • frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
    • description 有足够长度说明任务边界。The description is long enough to explain the task boundary.
    • description 已包含使用场景或排除边界信号。The description includes usage-scenario or exclusion-boundary signals.
    证据充分Evidence depth100
    • 已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.
    • skill-ir.json 已存在。skill-ir.json exists.
    • compiled_targets.json 已存在。compiled_targets.json exists.
    可维护性Maintainability100
    • SKILL.md 约 314 个词/字。SKILL.md is about 314 words/characters.
    • 入口文件保持克制,可维护性较好。The entrypoint stays restrained, which supports maintainability.
    • references/ 已承载扩展指导。references/ carries extended guidance.
    可迁移性Portability100
    • agents/interface.yaml 已存在。agents/interface.yaml exists.
    • manifest.json 已存在。manifest.json exists.
    • 目标平台或 adapter target 已声明。Target platforms or adapter targets are declared.
    上下文成本Context cost42
    • 入口约 314 个词/字,references 约 16934 个词/字。Entrypoint is about 314 words/characters; references are about 16934.
    • 分数越高代表上下文成本越低。A higher score means lower context cost.
    • 上下文成本偏高,建议压缩入口或拆分 references。Context cost is high; compress the entrypoint or split references further.
    +
    +
    +
    +
    + +
    +
    +
    +

    能力画像Capability

    +

    判断这个 Skill 在能力地图中的位置和复用范围。Places this skill on a capability map and clarifies reuse scope.

    +
    +
    +
    能力矩阵Capability Matrix执行确定性Execution certainty知识密度Knowledge densityExecution operationExecution operation
    能力矩阵说明这个 Skill 更偏知识密集还是执行确定。The capability matrix shows whether the skill leans toward knowledge density or execution certainty.
    +
    +

    画像摘要Profile

    +
    • 能力类型:Execution operationCapability type: Execution operation
    • 成熟度:governedMaturity: governed
    • 触发强度:手动触发 + description 路由Trigger strength: Manual activation plus description-based routing
    • 复用范围:跨平台Reuse scope: Cross-platform
    +
    +
    +
    +
    + +
    +
    +
    +

    原理结构Principle

    +

    说明入口、参考、脚本、评估和报告如何组成一个稳定闭环。Explains how entrypoint, references, scripts, evals, and reports form a stable loop.

    +
    +
    +
    分层结构Layered Structure入口层Entrypoint layer参考层Reference layer脚本层Script layer评估层Evaluation layer报告层Report layer
    分层结构展示入口、参考、脚本、评估和报告如何各司其职。The layered structure shows how entrypoint, references, scripts, evals, and reports each carry a distinct role.
    +
    +
    +

    执行流程Execution Flow

    +
    1. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.
    2. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Capture job, output, exclusions, constraints, standards, lightest fit.
    3. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.
    4. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.
    5. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned.
    +
    +
    +

    调用方式How To Use

    +
    1. 当你需要把工作流或笔记沉淀成结构精简、触发清晰且带必要 eval 的可复用 Skill 时使用 $yao-meta-skill。Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.
    2. Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.Use this skill when the request matches the frontmatter description.
    +
    +
    +
    +
    +
    + +
    +
    +
    +

    契约边界Contract

    +

    把触发、输入、输出和排除场景放在同一屏。Keeps trigger, inputs, outputs, and exclusions on the same screen.

    +
    +
    +
    +

    触发描述Trigger

    +

    Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.

    +

    输入材料Inputs

    +
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。an existing skill directory that needs refactor, evaluation, packaging, or governance hardening
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。benchmark references, local constraints, desired maturity tier, and review standards
    +
    +
    +

    输出结果Outputs

    +
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.
    • 结构化 Skill 目录,共 8 类关键资产。Structured skill directory with 8 key asset groups.
    +

    不应触发Should Not Trigger

    +
    • 只需要一次性回答、没有复用价值的临时请求。One-off requests that do not need reusable skill behavior.
    • 要求直接执行相邻任务,而不是沉淀或使用这个 Skill。Requests to perform an adjacent task directly rather than create or use this skill.
    • 缺少必要事实且用户不允许澄清的场景。Cases that lack required facts and do not allow clarification.
    +
    +
    +
    +
    + +
    +
    +
    +

    质量评估Quality

    +

    展示强项、缺口和建议,避免只给分不解释。Shows strengths, gaps, and recommendations instead of scores without explanation.

    +
    +
    + + + +
    类型Type证据Evidence建议Action
    强项Strength触发面保持精简,并锚定在 frontmatter description。The trigger surface stays lean and anchored in the frontmatter description.保留并复用Keep
    强项Strength已生成 Skill IR,核心语义可先于平台打包被审查和迁移。Skill IR is generated so core semantics can be reviewed and migrated before platform packaging.保留并复用Keep
    强项Strength已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。Target compilation evidence is generated to review how IR maps to OpenAI, Claude, generic, and other target contracts.保留并复用Keep
    缺口Gap上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。Context cost needs improvement: Entrypoint is about 314 words/characters; references are about 16934.纳入下一轮修复Fix next
    +
    +
    +

    执行证据Execution Evidence

    +
    • 变体运行:10Variant runs: 10
    • 模型执行:10Model executions: 10
    • 记录样本:0Recorded fixtures: 0
    • Token 估算:0Token estimates: 0
    +
    +
    +

    盲评审定Blind Adjudication

    +
    • 评审进度:0 / 5Review progress: 0 / 5
    • 待评审:5Pending review: 5
    • 一致率:暂无Agreement rate: not available yet
    • 非法决策:0Invalid decisions: 0
    +
    +
    +
    +
    +

    评审原则Review Rule

    +
    • 先记录 reviewer 对 A/B 的选择,再打开答案 key 计算一致率。Record the reviewer's A/B choice before opening the answer key and calculating agreement.
    • 缺少真实 reviewer 决策时只显示待评审,不伪造人工结论。When real reviewer decisions are missing, show pending status instead of fabricating human conclusions.
    +
    +
    +

    运行原则Run Rule

    +
    • recorded fixture 只能证明可复现样本,不等同于模型执行。A recorded fixture proves reproducible samples only; it is not model execution.
    • 只有 provider runner 返回 model metadata 时才计入 model-executed。Only provider runners that return model metadata count as model-executed.
    +
    +
    +
    +
    +
    + +
    +
    +
    +

    风险治理Risk

    +

    提前暴露误触发、漂移、证据不足和迁移风险。Surfaces trigger, drift, evidence, and portability risks before the package grows.

    +
    +
    +
    风险热力Risk Heatmap000200300发生概率Probability影响程度Impact
    风险热力图用影响程度和发生概率标出当前治理重点。The risk heatmap marks governance priorities by impact and probability.
    +
    + + + +
    风险Risk信号Signal应对Response
    误触发风险Trigger riskfrontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    输出漂移风险Output drift risk已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    证据不足风险Evidence gap risk已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    包体膨胀风险Package bloat riskSKILL.md 约 314 个词/字。SKILL.md is about 314 words/characters.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    跨平台迁移风险Portability riskagents/interface.yaml 已存在。agents/interface.yaml exists.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    +
    +
    +

    世界证据World Evidence

    世界级证据尚未完成:4 项待补,0 项已接受。World-class evidence is not complete: 4 pending, 0 accepted.

    证据待补Evidence pending
    待补证据Pending4仍需外部或人工证据接受。External or human evidence still needs acceptance.
    已接受Accepted0已通过 source check 与提交契约。Passed source checks and submission contract.
    源检查Source Checks12 / 19通过数 / 总检查数。Passed checks / total checks.
    外部证据External evidence

    提供商留出Provider Holdout

    缺少真实 provider 模型运行和 token metadata。Missing a real provider model run and token metadata.

    阻塞检查Blocked Checks

    当前没有阻塞检查。No blocked checks right now.

    人工证据Human evidence

    人工盲评Human Adjudication

    盲评 pair 仍待真实 reviewer 决策。Blind-review pairs still need real reviewer decisions.

    阻塞检查Blocked Checks
    • 无待判定No pending decisions
    • 盲评完成Judgments complete
    • Reviewer metadataReviewer metadata
    外部证据External evidence

    原生权限Native Permission

    原生 runtime enforcement 仍待目标客户端或外部安装器证明。Native runtime enforcement still needs target-client or external-installer proof.

    阻塞检查Blocked Checks
    • 原生执行Native enforcement
    外部证据External evidence

    原生遥测Native Telemetry

    真实外部客户端 metadata-only 事件仍未导入。Real external-client metadata-only events have not been imported yet.

    阻塞检查Blocked Checks
    • 外部事件External events
    +
    +
    + +
    +
    +
    +

    包体资产Assets

    +

    让 reviewer 快速确认关键文件、目录和资产分布。Lets reviewers confirm key files, directories, and asset distribution quickly.

    +
    +
    +
    资产分布Asset Distribution393项393 itemsSKILL.mdSKILL.mdREADME.mdREADME.mdagents/interface.yamlagents/interface.yamlmanifest.jsonmanifest.jsonreferencesreferencesscriptsscripts
    资产分布图展示当前包体的文件和目录重心。The asset distribution chart shows where files and directories are concentrated.
    + + + +
    路径Path作用Role类型Type
    SKILL.mdSkill 入口文件Skill entrypoint文件file
    README.md人类可读使用说明Human-readable usage guide文件file
    agents/interface.yaml跨平台接口元数据Neutral interface metadata文件file
    manifest.json生命周期与打包元数据Lifecycle and portability metadata文件file
    references扩展指导与复用资料Extended guidance and reusable notes目录folder
    scripts确定性脚本或本地工具Deterministic helpers or local tooling目录folder
    evals触发与质量检查Trigger and quality checks目录folder
    reports生成的证据与总结报告Generated evidence and overview artifacts目录folder
    +
    +
    +
    + +
    +
    +
    +

    迭代路线Roadmap

    +

    把下一步升级收束为少数高价值动作。Keeps next iteration moves focused and actionable.

    +
    +
    +
    迭代时间Iteration Timeline下一步 1Next 1补齐世界证据Close world-class evide...下一步 2Next 2Borrow one proven…Borrow one proven…下一步 3Next 3Harden portabilit…Harden portabilit…
    迭代时间线把下一步升级收束成少数可执行动作。The iteration timeline narrows the next upgrade into a few executable moves.
    +
    下一步 1Next 1

    补齐世界证据Close world-class evidence

    世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。World-class evidence still has 4 pending item(s); public completion claims must stay blocked.

    • 补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。Close provider holdout evidence: Missing a real provider model run and token metadata.
    • 补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。Close human adjudication evidence: Blind-review pairs still need real reviewer decisions.
    • 按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。Close all 4 pending evidence item(s) in the ledger (external 3, human 1; 2 not shown here) and keep the claim guard pending.

    全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。Only after the ledger accepts all external and human evidence should the public world-class claim move to review.

    下一步 2Next 2

    有选择地借鉴一个成熟模式Borrow one proven pattern on purpose

    已经有公开 benchmark 对象,下一步应主动选择一个模式借鉴,而不是松散吸收所有做法。You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.

    • 阅读 `obra/superpowers` 中最值得借鉴的模式。Read the strongest pattern from obra/superpowers.
    • 先判断要借鉴的是方法、结构、执行方式还是可迁移性,并且第一轮只借鉴其中一个。Decide whether to borrow method, structure, execution, or portability, but only one of them first.
    • 记录本轮不借鉴的内容,避免包体过重。Record what you will not borrow so the package stays light.

    包体形态更清晰,也减少偶然过度设计。A cleaner package shape with less accidental over-design.

    下一步 3Next 3

    加固跨环境语义Harden portability semantics

    这个 Skill 已经面向跨环境复用,因此早期就需要把契约语义说清楚。The skill already signals reuse across environments, so contract clarity matters early.

    • 确认激活模式、执行上下文和信任假设。Confirm activation mode, execution context, and trust assumptions.
    • 补充或复核非原生目标端的降级策略。Add or review degradation strategy for non-native targets.
    • 至少打包一次 Skill,用来验证 adapter 预期。Package the skill once to verify adapter expectations.

    跨环境复用更安全,目标漂移更少。Safer cross-environment reuse with less target drift.

    +
    +
    +
    +
    + + + diff --git a/reports/skill-interpretation.json b/reports/skill-interpretation.json new file mode 100644 index 0000000..a190723 --- /dev/null +++ b/reports/skill-interpretation.json @@ -0,0 +1,2296 @@ +{ + "skill_summary": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "maturity": "governed", + "updated_at": "2026-03-31", + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": [ + "SKILL.md", + "agents/interface.yaml", + "skill-ir/examples/yao-meta-skill.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_adjudication.md", + "reports/benchmark_reproducibility.md", + "reports/output_blind_answer_key.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/world_class_evidence_plan.md", + "reports/world_class_evidence_ledger.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-interpretation.html", + "reports/skill-overview.html", + "reports/skill-interpretation.json" + ], + "flow": [ + "输入材料", + "Skill 包体", + "可复用能力" + ] + }, + "scorecard": { + "completeness_score": { + "label": "完整度", + "score": 100, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 100, + "reasons": [ + "已生成 20 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 100, + "reasons": [ + "SKILL.md 约 314 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 已承载确定性逻辑。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 42, + "reasons": [ + "入口约 314 个词/字,references 约 16934 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本偏高,建议压缩入口或拆分 references。" + ] + } + }, + "capability_profile": { + "archetype": "governed", + "task_family": "Execution operation", + "maturity": "governed", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "principle_model": { + "nodes": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "contract_boundary": { + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "quality_review": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "gaps": [ + "上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + } + }, + "risk_governance": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 1, + "signal": "SKILL.md 约 314 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "world_class_readiness": { + "ready": false, + "decision": "evidence-pending", + "entry_count": 4, + "pending_count": 4, + "accepted_count": 0, + "external_pending_count": 3, + "human_pending_count": 1, + "source_check_count": 19, + "source_pass_count": 12, + "conclusion_zh": "世界级证据尚未完成:4 项待补,0 项已接受。", + "conclusion_en": "World-class evidence is not complete: 4 pending, 0 accepted.", + "entries": [ + { + "key": "provider-holdout", + "label_zh": "提供商留出", + "label_en": "Provider Holdout", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "缺少真实 provider 模型运行和 token metadata。", + "summary_en": "Missing a real provider model run and token metadata.", + "blocked_checks": [] + }, + { + "key": "human-adjudication", + "label_zh": "人工盲评", + "label_en": "Human Adjudication", + "category": "human", + "category_zh": "人工证据", + "category_en": "Human evidence", + "status": "pending", + "summary_zh": "盲评 pair 仍待真实 reviewer 决策。", + "summary_en": "Blind-review pairs still need real reviewer decisions.", + "blocked_checks": [ + "No pending decisions", + "Judgments complete", + "Reviewer metadata" + ] + }, + { + "key": "native-permission-enforcement", + "label_zh": "原生权限", + "label_en": "Native Permission", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "原生 runtime enforcement 仍待目标客户端或外部安装器证明。", + "summary_en": "Native runtime enforcement still needs target-client or external-installer proof.", + "blocked_checks": [ + "Native enforcement" + ] + }, + { + "key": "native-client-telemetry", + "label_zh": "原生遥测", + "label_en": "Native Telemetry", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "真实外部客户端 metadata-only 事件仍未导入。", + "summary_en": "Real external-client metadata-only events have not been imported yet.", + "blocked_checks": [ + "External events" + ] + } + ] + }, + "package_assets": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "file_count": 393, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 35 + }, + { + "label": "scripts", + "value": 155 + }, + { + "label": "evals", + "value": 29 + }, + { + "label": "reports", + "value": 170 + } + ] + }, + "iteration_roadmap": { + "items": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ] + }, + "report_contract": { + "schema_version": "2.0", + "html_report": "reports/skill-interpretation.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "layout": "kami-white-audit-v2", + "nav_labels": [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线" + ], + "nav_labels_en": [ + "Overview", + "Metrics", + "Profile", + "Principle", + "Contract", + "Quality", + "Risk", + "Assets", + "Roadmap" + ], + "report_kind": "skill-interpretation", + "canonical_overview_report": "reports/skill-overview.html", + "json_report": "reports/skill-interpretation.json", + "purpose": "Explain the generated skill's role, principles, usage scenarios, trigger contract, inputs, outputs, quality evidence, risks, assets, highlights, and next upgrade directions." + }, + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "logic_steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "usage_steps": [ + "Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.", + "Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + ], + "package_map": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "scenarios": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals." + ], + "trigger_contract": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "io_contract": { + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ] + }, + "principles": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "roadmap": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ], + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。" + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": "2.0.0", + "source_path": "skill-ir/examples/yao-meta-skill.json", + "target_count": 5, + "trigger_samples": 8, + "output_eval_cases": 3 + }, + "compiled_targets": { + "ok": true, + "schema_version": "1.0", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter_mode": "metadata-adapter", + "degradation_strategy": "metadata-adapter", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "permission_enforcement": "metadata-only", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "adapter_mode": "neutral-source-plus-adapter", + "degradation_strategy": "neutral-source-plus-adapter", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "permission_enforcement": "metadata-fallback", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [] + }, + { + "target": "generic", + "status": "pass", + "adapter_mode": "agent-skills-compatible", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills compatible neutral package", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "targets/generic/adapter.json" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "agent-skills-compatible", + "status": "pass", + "adapter_mode": "neutral-agent-skills-source", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills standard source tree", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "vscode", + "status": "pass", + "adapter_mode": "vscode-agent-skills-adapter", + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "permission_enforcement": "client-or-workspace-trust", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [] + } + ], + "failures": [], + "warnings": [] + }, + "output_quality": { + "case_count": 5, + "file_backed_case_count": 1, + "near_neighbor_case_count": 1, + "boundary_case_count": 1, + "baseline_pass_rate": 0.0, + "with_skill_pass_rate": 100.0, + "delta": 100.0, + "regression_count": 0, + "gate_pass": true, + "failure_taxonomy": [], + "blind_pair_count": 5 + }, + "output_execution": { + "ok": true, + "summary": { + "case_count": 5, + "variant_run_count": 10, + "command_executed_count": 10, + "model_executed_count": 10, + "recorded_fixture_count": 0, + "timing_observed_count": 10, + "token_observed_count": 10, + "token_estimated_count": 0, + "baseline_pass_rate": 23.67, + "with_skill_pass_rate": 43.67, + "delta": 20.0, + "regression_count": 0, + "failure_count": 0, + "gate_pass": true + }, + "runner": { + "mode": "command", + "command": [ + "python3", + "scripts/provider_output_eval_runner.py", + "--provider", + "deepseek", + "--model", + "deepseek-v4-flash", + "--api-format", + "chat-completions", + "--thinking", + "disabled", + "--api-key-env", + "DEEPSEEK_API_KEY" + ], + "timeout_seconds": 180.0 + }, + "failures": [] + }, + "output_blind_review": { + "summary": { + "pair_count": 5, + "answer_key_separate": true, + "with_skill_hidden_count": 5 + }, + "seed": "yao-output-eval-blind-v1", + "pair_count": 5, + "answer_key_separate": true + }, + "output_review_kit": { + "ok": true, + "summary": { + "case_count": 5, + "ready_for_adjudication_count": 0, + "pending_decision_count": 5, + "invalid_decision_count": 0, + "reviewer_metadata_present": false, + "answer_key_hidden": true, + "answer_key_path_exposed": false, + "ready_to_run_adjudication": false, + "failure_count": 0 + }, + "artifacts": { + "reviewer_kit_json": "reports/output_review_kit.json", + "reviewer_kit_markdown": "reports/output_review_kit.md", + "reviewer_kit_html": "reports/output_review_kit.html", + "blind_pack_json": "reports/output_blind_review_pack.json", + "blind_pack_markdown": "reports/output_blind_review_pack.md", + "decisions": "reports/output_review_decisions.json", + "adjudication_json": "reports/output_review_adjudication.json", + "adjudication_markdown": "reports/output_review_adjudication.md" + }, + "failures": [] + }, + "output_review_adjudication": { + "ok": true, + "summary": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "agreement_count": 0, + "disagreement_count": 0, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "pending_answer_hidden_count": 5, + "agreement_rate": null, + "needs_review": true, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "failure_count": 0, + "reviewer_checklist_count": 5, + "reviewer_checklist_pending_count": 5, + "reviewer_checklist_invalid_count": 0, + "reviewer_checklist_ready_count": 0 + }, + "reviewer": "", + "reviewed_at": "", + "failures": [] + }, + "benchmark_reproducibility": { + "ok": true, + "summary": { + "reproducibility_ready": true, + "release_lock_ready": false, + "methodology_complete": true, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "evidence_bundle_sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158", + "source_contract_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "output_case_count": 5, + "failure_disclosure_count": 3, + "command_count": 23, + "command_executed_count": 10, + "timing_observed_count": 10, + "model_executed_count": 10, + "token_observed_count": 10, + "human_review_complete": false, + "provider_evidence_complete": true, + "world_class_ready": false, + "world_class_open_gap_count": 3, + "world_class_task_count": 4, + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4, + "working_tree_dirty": true, + "changed_file_count": 37, + "source_tree_dirty": true, + "source_changed_file_count": 1, + "generated_tree_dirty": true, + "generated_changed_file_count": 36 + }, + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "missing_artifacts": [], + "limitations": [ + "The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately.", + "Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet.", + "Pending blind-review decisions are visible but do not count as human adjudication.", + "World-class readiness remains false until external and human evidence gaps close.", + "Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims." + ] + }, + "runtime_conformance": { + "target_count": 5, + "pass_count": 5, + "fail_count": 0 + }, + "runtime_permissions": { + "ok": true, + "summary": { + "target_count": 4, + "pass_count": 4, + "fail_count": 0, + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "residual_risk_count": 4, + "required_capability_count": 3, + "failure_count": 0, + "installer_enforcement_source_status": "present", + "installer_enforcement_target_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "installer_permission_capability_count": 3, + "world_class_native_evidence_ready": false, + "installer_enforcement_ready": true + }, + "expected_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "targets": [ + { + "target": "openai", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "claude", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "generic", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "vscode", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + } + ], + "failures": [] + }, + "trust_security": { + "scanned_files": 247, + "script_count": 155, + "internal_module_count": 69, + "secret_findings": 0, + "dependency_files": [ + "requirements-ci.txt" + ], + "network_script_count": 3, + "network_policy_covered_count": 3, + "network_policy_missing_count": 0, + "file_write_script_count": 74, + "permission_required_count": 3, + "permission_approved_count": 3, + "permission_missing_count": 0, + "permission_invalid_count": 0, + "permission_expired_count": 0, + "help_smoke_checked_count": 86, + "help_smoke_failed_count": 0, + "interactive_script_count": 0, + "package_hash_scope": "source-contract-without-generated-reports", + "package_hash_file_count": 247, + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + }, + "skill_atlas": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + }, + "registry_distribution": { + "ok": true, + "package": { + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "agent-skills": "pass", + "vscode": "pass", + "generic": "pass", + "agent-skills-compatible": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "package_verification": "reports/package_verification.json", + "install_simulated": true, + "install_simulation": "reports/install_simulation.json" + }, + "generated_at": "2026-06-13" + }, + "failures": [], + "warnings": [] + }, + "package_verification": { + "ok": true, + "summary": { + "target_count": 4, + "adapter_count": 4, + "archive_present": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "archive_entry_count": 580, + "nested_skill_entry_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "install_simulation": { + "ok": true, + "summary": { + "archive_present": true, + "archive_entry_count": 598, + "nested_skill_entry_count": 0, + "archive_extracted": true, + "entrypoint_loaded": true, + "manifest_loaded": true, + "interface_loaded": true, + "adapter_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "install_root_is_temp": false, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "upgrade_check": { + "ok": true, + "summary": { + "declared_bump": "minor", + "recommended_bump": "minor", + "breaking_change_count": 0, + "added_target_count": 2, + "metadata_change_count": 1, + "checksum_change_count": 2, + "failure_count": 0, + "warning_count": 0 + }, + "upgrade_diff": { + "name_changed": false, + "added_targets": [ + "agent-skills-compatible", + "vscode" + ], + "removed_targets": [], + "compatibility_changes": [ + { + "target": "agent-skills", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "agent-skills-compatible", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "vscode", + "from": "missing", + "to": "pass", + "direction": "improved" + } + ], + "metadata_changes": [ + { + "field": "description", + "from": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes.", + "to": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + } + ], + "checksum_changes": [ + { + "field": "archive_sha256", + "from": "", + "to": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + { + "field": "package_sha256", + "from": "0000000000000000000000000000000000000000000000000000000000000000", + "to": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "release_notes": [ + "Recommended version bump: minor.", + "Added targets: agent-skills-compatible, vscode.", + "Package or archive checksum changed; reviewers should verify package artifacts before release.", + "Package verification evidence: reports/package_verification.md." + ], + "failures": [], + "warnings": [] + }, + "adoption_drift": { + "ok": true, + "summary": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 100.0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "failure_types": {}, + "source_types": { + "manual": 1 + }, + "command_counts": {} + }, + "next_iteration_candidates": [], + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "failures": [] + }, + "review_waivers": { + "ok": true, + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 2, + "waiverable_open_count": 1, + "non_waivable_count": 1 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "review_studio_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "review-waivers", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "world-class-evidence" + ], + "known_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "waiverable_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "non_waivable_gate_keys": [ + "review-waivers", + "world-class-evidence" + ] + }, + "failures": [], + "warnings": [] + }, + "review_annotations": { + "ok": true, + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [] + }, + "world_class_evidence_plan": { + "ok": true, + "summary": { + "audit_decision": "continue-iteration", + "world_class_ready": false, + "audit_world_class_ready": false, + "ready_to_claim_world_class": false, + "ledger_completion_required": true, + "evidence_requirement_count": 4, + "task_count": 4, + "human_task_count": 1, + "external_task_count": 3, + "review_task_count": 0, + "decision": "collect-external-evidence" + }, + "tasks": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "status": "pass", + "category": "external", + "owner": "operator with provider credentials", + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "audit_next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "category": "human", + "owner": "human reviewer", + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "audit_next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "category": "external", + "owner": "target client or installer integrator", + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "audit_next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "audit_next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_audit": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "open_gap_count": 3 + } + }, + "world_class_evidence_ledger": { + "ok": true, + "summary": { + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "entries": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "pending", + "source_status": "pass", + "source_accepted": true, + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "pending", + "source_status": "human_required", + "source_accepted": false, + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_plan": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "task_count": 4, + "evidence_requirement_count": 4 + } + }, + "synthesis_highlights": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + }, + "metadata": { + "canonical_format": "agent-skills", + "targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "maturity_tier": "governed", + "skill_archetype": "governed", + "updated_at": "2026-03-31" + }, + "interpretation_contract": { + "schema_version": "2.0", + "source_model": "skill-overview-v2", + "source_model_reused": true, + "overview_report": "reports/skill-overview.html", + "html_report": "reports/skill-interpretation.html", + "json_report": "reports/skill-interpretation.json", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "includes": [ + "skill role", + "principles", + "usage scenarios", + "trigger contract", + "inputs and outputs", + "quality evidence", + "risk governance", + "package assets", + "highlights", + "upgrade roadmap" + ] + } +} \ No newline at end of file diff --git a/reports/skill-os-2-review.md b/reports/skill-os-2-review.md new file mode 100644 index 0000000..51af1f9 --- /dev/null +++ b/reports/skill-os-2-review.md @@ -0,0 +1,165 @@ +# Skill OS 2.0 Review + +Review date: 2026-06-13 +Scope: Yao Meta Skill against the user-provided Skill OS 2.0 upgrade plan. + +## Verdict + +Yao Meta Skill is no longer only a Meta Skill factory. The current working tree now has the first verifiable Skill OS foundation: + +- Skill IR v0 for platform-neutral contract capture. +- Output Eval Lab v0 for with-skill vs baseline assertion grading plus local command-runner execution evidence, blind A/B review pack, separate answer key evidence, reviewer decision import, and adjudication reporting. +- Runtime Conformance v0 for target-consumption checks. +- Trust/Security v0 for secret, dependency, script, bounded network host policy, execution-level CLI help smoke checks, trust metadata, and stable source-contract integrity checks. +- Skill Atlas v0 for portfolio catalog, route overlap, stale ownership, dependency signals, aggregate drift signals, and no-route opportunities. +- Bilingual Skill Overview v2 that includes these evidence surfaces. +- Skill Overview Asset Split v0 so long report CSS and JavaScript live in `assets/skill-overview.css` and `assets/skill-overview.js`, while the Python layout module keeps the generated report single-file by inlining those assets at render time. +- Review Studio Source Anchor Excerpts v0 so every non-pass action source ref carries a matched pattern and short line excerpt, letting reviewers understand the anchor before opening the full artifact. +- Review Studio World-Class Collection Contract v0 so each pending world-class evidence action card shows provenance requirements, success checks, evidence artifacts, and privacy boundaries alongside commands and blocked checks. +- Review Studio Asset Split v0 so long Review Studio CSS lives in `assets/review-studio.css`, while the Python layout module still inlines it into the generated static HTML report. +- Skill OS 2.0 Audit v0 for requirement-by-requirement evidence mapping, with explicit separation between local evidence, human-required review, and external-required provider/client integrations. +- World-Class Evidence Plan v0 that turns the remaining provider holdout, human adjudication, native permission enforcement, and native client telemetry gaps into executable runbooks with success checks and privacy contracts. +- World-Class Evidence Ledger v0 that records current acceptance state, provenance requirements, privacy contracts, and anti-overclaim guards for those remaining gaps. +- World-Class Intake Contract Hardening v0 so real evidence submissions must use the ledger's canonical `.json` filename and are recursively rejected when they include raw prompt, output, transcript, message, credential, secret, token, or API-key fields. +- World-Class Human Evidence Guard v0 so human-adjudication decision artifacts recursively reject raw content, credential, secret, token, and answer-key fields before blind A/B review evidence can be accepted. +- Output Review Privacy Guard v0 so the blind-review importer and world-class human evidence validator share one recursive blocked-field contract for raw content, credential, secret, token, and answer-key leakage. +- World-Class Submission Privacy Guard v0 so real evidence submission packets reuse the same blocked-field contract and reject nested answer-key leakage before ledger review. +- World-Class Provider Evidence Guard v0 so provider holdout submissions must reconcile `summary` counts with `runs` rows and include at least one passing model run whose provider, model, timing, non-estimated usage, and output hash match the submitted provenance. +- World-Class Native Permission Evidence Guard v0 so native-permission submissions must reconcile runtime probe summary counts with target rows, require at least one native-enforced target row, and keep installer permission checks failure-free. +- World-Class Native Telemetry Evidence Guard v0 so native-client-telemetry submissions must reconcile adoption summary counts with external metadata event rows and keep hook recipes metadata-only without claiming native auto-capture. +- World-Class Submission Matrix v0 so the submission kit exposes a single operator-facing matrix for draft status, artifact readiness, source-check blockers, and the next action without counting matrix rows as completion evidence. +- World-Class Repair Checklist v0 so the submission kit turns every draft, artifact, and source-check blocker into machine-readable repair rows without counting repair guidance as completion evidence. +- World-Class Preflight Repair Rows v0 so the collection preflight exposes required precheck and source-check blockers as machine-readable operator actions before evidence submission. +- World-Class Submission Artifact Roles v0 so submission kits distinguish `submission-ref` rows that belong in `artifact_refs` from supporting audit assets, reducing operator ambiguity without accepting evidence. +- World-Class Submission Kit Rendering v0 so Markdown and HTML kit presentation lives in a dedicated internal renderer, keeping the CLI focused on evidence assembly and file emission. +- Benchmark Reproducibility v0 that turns public benchmark methodology, required artifacts, failure disclosure, and reproduction commands into machine-checkable release evidence. +- Review Studio 2.0 v0 for one-page blocker, warning, evidence-path, review-action, and release-gate review. +- Review Studio Source Refs v0 so every non-pass review action can expose structured relative source/report links with best-effort line numbers. +- Target Compiler v0 so Skill IR compiles into OpenAI, Claude, generic, Agent Skills compatible, and VS Code / Copilot target contracts before packaging, including target-specific permission contracts and native behavior contracts. +- IR-first packaging v0 so adapters carry the platform-neutral semantic contract, compiler contract, parity checks, IR provenance, and VS Code / Copilot install-scope notes where applicable. +- Registry & Distribution v0 for package metadata, checksum, owner, license, compatibility matrix, and audit reporting. +- Package Verification v0 for generated manifest, target adapters, zip archive safety, archive checksum, and registry parity. +- Install Simulation v0 for local extraction, entrypoint loading, interface loading, report presence, adapter readability, and installer-level permission approval/enforcement checks. +- Upgrade Check v0 for registry package diffs, semver bump recommendations, breaking-change notes, and release evidence. +- Adoption Drift v0 for local-first metadata-only telemetry, privacy-field blocking, usage-signal aggregation, iteration candidate generation, and Skill Atlas drift-signal input. +- CLI Telemetry Capture v0 for opt-in `yao.py` command-run metadata that records command name, source, outcome, failure class, and timestamp without command arguments or raw user/model content. +- External Telemetry Import v0 for whole-file validated metadata-only JSONL ingestion from non-CLI clients, with dry-run support and all-or-nothing rejection on raw content fields or schema violations. +- Telemetry Emit Hook v0 for Browser/Chrome/IDE/wrapper integrations to append one validated metadata-only external event into a local spool before import, with dry-run validation and raw-content blocking. +- Telemetry Hook Recipes v0 for Browser, Chrome, VS Code, CLI wrapper, and provider-adapter recipe generation with dry-run commands, metadata-only privacy checks, and explicit non-claiming of native host integration. +- Telemetry Native Host v0 for Browser/Chrome Native Messaging length-prefixed stdio, metadata-only event validation, local launcher generation, and Chrome host manifest generation. +- Review Waivers v0 for human warning acceptance with reviewer, reason, scope, expiry, and blocker-safe policy. +- Governed Permission Gates v0 for reviewer-approved network, file-write, and subprocess capabilities with scope, reason, expiry, evidence, and target-enforcement mapping. +- Runtime Permission Probes v0 for packaged target adapter checks, explicit native-enforcement flags, metadata fallback evidence, and residual permission risks. +- Local Install Sync Preflight v0 so source-to-local and source-to-active install syncs rebuild the package first, run install simulation, enforce installer permission coverage, and refuse to copy files before any destructive sync when the package is not install-ready. +- Atlas Scope Policy v0 so examples, evolution snapshots, embedded generated skills, and validator fixtures remain visible in the full portfolio report without polluting release-actionable gates. +- Review Annotations v0 for reviewer comments tied to Review Studio gates, source/report paths, and optional line numbers; open blocker annotations now block the Review Studio decision. +- Review Studio now avoids over-claiming release readiness when blind A/B adjudication, waiver handling, and world-class evidence are still pending: the root Meta Skill is in `review` with score `91`, no blockers, `3` warnings, and explicit actions for Output Lab reviewer adjudication, waiver handling, and world-class evidence completion. +- Review Studio Output Lab actions now link directly to `reports/output_review_decisions.json`, so pending blind A/B reviewer decisions have a concrete template instead of only a general adjudication warning. +- Output Review Adjudication now preserves blind-review integrity by hiding expected winners for pending or invalid reviewer decisions; answer keys are revealed only after a valid A/B decision exists for that case. +- Output Review Import v0 now accepts reviewer JSON, JSONL, or CSV decision sources, recursively rejects raw prompt/output/transcript/message, credential, secret, token, and answer-key fields case-insensitively, writes canonical `reports/output_review_decisions.json`, and can run adjudication immediately without opening the answer key before valid decisions exist. +- Provider Output Eval Runner v0 so `python3 scripts/yao.py output-exec --provider-runner openai` can collect real provider-backed model evidence through a reviewed OpenAI Responses API compatible runner instead of ad hoc shell glue. +- Weekly SkillOps Curator v0 so daily SkillOps opportunities, Skill Atlas portfolio signals, release-lock state, and world-class evidence gaps roll up into a proposal-only weekly maintenance queue without source-file writes. + +This is still not the final world-class state. Target-native behavior contracts are now explicit, VS Code / Copilot package metadata is auditable, provider-backed output-eval execution is wired through DeepSeek evidence, blind-review answers remain hidden until valid decisions exist, a provider-backed output runner exists, installer-level permission coverage is now locally enforced during install simulation and local install sync, opt-in `yao.py` CLI telemetry can capture metadata-only real run signals, external clients can now emit one validated metadata-only event into a local spool, hook recipes now make Browser/Chrome/VS Code/wrapper/provider-adapter integration commands auditable, a Browser/Chrome Native Messaging host can receive length-prefixed metadata events and generate a local launcher plus manifest, validated external JSONL imports can bring those non-CLI client signals into the same drift loop, and Skill Atlas now consumes aggregate drift reports. World-class intake now rejects non-canonical submission filenames and nested raw content or credential-like fields before ledger review. The preflight and submission kit now give operators repair rows, matrix state, artifact readiness, source blockers, and next actions for each pending world-class evidence key. Review Studio keeps pending human adjudication, waiver handling, and pending world-class evidence visible as warnings instead of treating them as a clean pass. Accepted external ledger evidence, real human adjudication decisions, installed platform-native client telemetry implementations, provider-native installer integration, and native runtime permission enforcement remain open. + +## Coverage Matrix + +| 2.0 Area | Current Evidence | Status | +| --- | --- | --- | +| Skill IR | `skill-ir/schema.json`, `skill-ir/examples/yao-meta-skill.json`, `scripts/export_skill_ir.py` | v0 landed | +| Target Compiler | `scripts/compile_skill.py`, `scripts/compile_skill_targets.py`, `reports/compiled_targets.md`, `tests/verify_compile_skill.py` | v0 landed | +| Output Eval Lab | `evals/output/cases.jsonl`, `scripts/run_output_eval.py`, `scripts/run_output_execution.py`, `scripts/local_output_eval_runner.py`, `scripts/provider_output_eval_runner.py`, `scripts/import_output_review_decisions.py`, `scripts/output_review_privacy.py`, `scripts/adjudicate_output_review.py`, `reports/output_quality_scorecard.md`, `reports/output_execution_runs.md`, `reports/output_blind_review_pack.md`, `reports/output_blind_answer_key.json`, `reports/output_review_decisions.json`, `reports/output_review_adjudication.md` | v0 landed | +| Benchmark methodology | `reports/benchmark_methodology.md` | v0 landed | +| Benchmark Reproducibility | `scripts/render_benchmark_reproducibility.py`, `reports/benchmark_reproducibility.md`, `tests/verify_benchmark_reproducibility.py` | v0 landed | +| Skill OS 2.0 Audit | `scripts/render_skill_os2_audit.py`, `reports/skill_os2_audit.md`, `tests/verify_skill_os2_audit.py` | v0 landed | +| World-Class Evidence Plan | `scripts/render_world_class_evidence_plan.py`, `reports/world_class_evidence_plan.md`, `tests/verify_world_class_evidence_plan.py` | v0 landed | +| World-Class Evidence Ledger | `scripts/render_world_class_evidence_ledger.py`, `reports/world_class_evidence_ledger.md`, `tests/verify_world_class_evidence_ledger.py` | v0 landed | +| World-Class Evidence Intake | `scripts/world_class_evidence_contract.py`, `scripts/world_class_human_evidence.py`, `scripts/output_review_privacy.py`, `scripts/world_class_provider_evidence.py`, `scripts/world_class_native_permission_evidence.py`, `scripts/world_class_native_telemetry_evidence.py`, `scripts/render_world_class_evidence_intake.py`, `evidence/world_class/intake.schema.json`, `tests/verify_world_class_evidence_intake.py` with canonical filename, source-artifact validation, recursive human decision privacy and answer-key validation, provider run-row validation, native permission target-row validation, native telemetry event-row validation, nested raw-field rejection, and real-submission answer-key leakage rejection | v0 landed | +| World-Class Submission Kit | `scripts/prepare_world_class_submission_kit.py`, `scripts/world_class_submission_matrix.py`, `scripts/world_class_repair_checklist.py`, `scripts/world_class_submission_kit_rendering.py`, `tests/verify_world_class_submission_kit.py` with draft, artifact, source-check, next-action matrix evidence, machine-readable repair rows, and separated Markdown/HTML rendering | v0 landed | +| Runtime Conformance | `scripts/run_conformance_suite.py`, `reports/conformance_matrix.md` | v0 landed | +| Trust & Security | `scripts/trust_check.py`, `reports/security_trust_report.md`, `security/*.md` | v0 landed | +| Review Studio 2.0 | `scripts/render_review_studio.py`, `reports/review-studio.html`, `reports/review-studio.json` with per-warning `review_actions` | v0 landed | +| Review Studio Source Refs | `scripts/render_review_studio.py`, `tests/verify_review_studio.py`, `references/review-studio-method.md` | v0 landed | +| Review Annotations | `scripts/render_review_annotations.py`, `reports/review_annotations.md`, `tests/verify_review_annotations.py`, Review Studio annotation panel and blocker decision hook | v0 landed | +| Weekly SkillOps Curator | `scripts/render_weekly_curator_report.py`, `reports/skillops/weekly/2026-W25.md`, `tests/verify_weekly_curator.py`, proposal-only maintenance queue with no source writes | v0 landed | +| Skill Atlas | `scripts/build_skill_atlas.py`, `skill_atlas/catalog.json`, `skill_atlas/route_overlap_matrix.csv`, `skill_atlas/drift_signals.json`, `reports/skill_atlas.html` | v0 landed | +| Registry & Distribution | `registry/*.schema.json`, `scripts/registry_audit.py`, `reports/registry_audit.md`, `registry/packages/yao-meta-skill.json` | v0 landed | +| Package Verification | `scripts/verify_package.py`, `reports/package_verification.md`, `tests/verify_package_verification.py` | v0 landed | +| Install Simulation | `scripts/simulate_install.py`, `reports/install_simulation.md`, `tests/verify_install_simulation.py` with installer permission coverage checks | v0 landed | +| Local Install Sync Preflight | `scripts/sync_local_install.py`, `tests/verify_local_install_sync.py`, `Makefile` package-check prerequisites | v0 landed | +| Upgrade Check | `scripts/upgrade_check.py`, `reports/upgrade_check.md`, `tests/verify_upgrade_check.py` | v0 landed | +| Telemetry & Drift | `scripts/render_adoption_drift_report.py`, `scripts/yao_cli_telemetry.py`, `scripts/emit_telemetry_event.py`, `scripts/render_telemetry_hook_recipes.py`, `scripts/telemetry_native_host.py`, `scripts/import_telemetry_events.py`, `reports/adoption_drift_report.md`, `reports/telemetry_hook_recipes.md`, `tests/verify_adoption_drift.py`, `tests/verify_telemetry_emit.py`, `tests/verify_telemetry_hooks.py`, `tests/verify_telemetry_native_host.py`, `tests/verify_telemetry_import.py`, `tests/verify_yao_cli.py`, `references/telemetry-drift-method.md` | v0 landed | +| Review Waivers | `scripts/render_review_waivers.py`, `reports/review_waivers.md`, `tests/verify_review_waivers.py`, `references/review-waiver-method.md` | v0 landed | +| Governed Permission Gates | `security/permission_policy.json`, `scripts/trust_check.py`, `scripts/render_review_studio.py`, `tests/verify_trust_check.py`, `tests/verify_review_studio.py` | v0 landed | +| Runtime Permission Probes | `scripts/probe_runtime_permissions.py`, `reports/runtime_permission_probes.md`, `tests/verify_runtime_permission_probes.py`, `tests/verify_review_studio.py` | v0 landed | +| Atlas Scope Policy | `skill_atlas/policy.json`, `scripts/build_skill_atlas.py`, `tests/verify_skill_atlas.py` | v0 landed | +| Compiler from IR | Packager consumes compiled target contracts for compiler provenance, generated files, adapter modes, permissions, preserved semantics, warnings, and unsupported features | v0 landed | +| Target Native Contracts | `scripts/compile_skill.py`, `scripts/compile_skill_targets.py`, `scripts/cross_packager.py`, `reports/compiled_targets.md`, adapter snapshots, and generated target outputs carry native surface, activation policy, resource/script strategy, permission enforcement, install scope, review artifacts, fallback behavior, and unsupported native features | v0 landed | + +## Top Findings + +### 1. Target-native behavior contracts are landed, but provider execution is still shallow + +`scripts/compile_skill.py` now reads Skill IR and emits target-specific contracts for OpenAI, Claude, generic, Agent Skills compatible, and VS Code / Copilot outputs, with target platform models and contract builders isolated in `scripts/compile_skill_targets.py`. The packager embeds `compiler`, `compiled_contract`, `permission_contract`, `target_permission_contract`, `target_native_contract`, `target_transform`, warnings, and unsupported-feature notes in each adapter. The native contract makes the target surface, activation policy, resource strategy, script strategy, permission enforcement mode, install scope, review artifacts, fallback behavior, and unsupported native features reviewable instead of implicit. + +Next move: deepen provider-native execution transforms so OpenAI, Claude, VS Code/Copilot, and generic packages can express and verify behavior through real client or installer capabilities, not only auditable contracts and fallback notes. + +### 2. Output eval now has provider-backed model evidence, but human review is still open + +The v0 cases now cover five scenarios, including near-neighbor and file-backed governed package cases. Each run emits assertion grading, execution provenance, a blind A/B review pack that hides baseline vs with-skill labels, a separate answer key, an importer for reviewer JSON/JSONL/CSV decision sources, and an adjudication report from reviewer choices. The current root execution report runs all `10` variants through `scripts/provider_output_eval_runner.py` against DeepSeek `deepseek-v4-flash`, so model execution, timing, and token usage are observed rather than estimated. The provider source checks now pass with `10` model-executed runs, `10` observed timing rows, `10` observed token rows, `0` estimated token rows, and no regressions, while the world-class ledger still requires an independent reviewer-approved submission before it counts as accepted evidence. The current root adjudication report intentionally records `0 / 5` human judgments pending instead of pretending human review happened. + +Next move: complete independent provider evidence ledger review, then record actual blind A/B reviewer decisions. + +### 3. Review Studio is unified and now has reviewer actions plus annotations + +The Review Studio page aggregates intent, trigger, output, context, runtime, trust, permission approvals, runtime permission probes, atlas, operations-loop, reviewer waiver, reviewer annotations, registry, world-class evidence, and release gates. It exposes current warnings directly and emits `review_actions` for each non-pass gate with a source-fix location, structured `source_refs`, reason, evidence path, and verification command. It now also loads `reports/review_annotations.json`, renders reviewer comments tied to gates and source/report paths, and blocks the page decision when any open blocker annotation exists. The current root report is intentionally not a clean pass: decision `review`, score `91`, `16` gates, `0` blockers, `3` warnings, `3` actions, and `0` open annotation blockers. The warnings are useful evidence: the automated Output Eval Lab has provider-backed execution but blind A/B adjudication still has `5` pending reviewer decisions, warning waivers still need reviewer handling, and the world-class ledger still has `4` pending external or human evidence entries, so the package should not claim fully reviewed or world-class status. + +Next move: add richer source-line anchors inside generated reports, record real reviewer annotations during the next human review pass, and close the provider ledger-review, native-permission, real-client telemetry, and human review evidence entries in the world-class ledger. + +### 4. Multi-skill operation now links Atlas with drift, but needs platform-native capture + +The new Skill Atlas can scan a workspace and report catalog, route overlap, dependency graph, stale skill, missing owner/review metadata, aggregate drift signals, and no-route opportunities. It now also supports `skill_atlas/policy.json` so release gates distinguish actionable library skills from examples and fixtures while keeping full visibility. Adoption Drift v0 can record metadata-only local events, block raw prompt/output fields, summarize missed-trigger, bad-output, script-error, and review-overdue signals, feed next iteration candidates into Review Studio, and publish aggregate drift input for Atlas. `yao.py` now adds opt-in automatic CLI run capture through `YAO_CLI_TELEMETRY=1` or `--record-cli-telemetry`, recording only `source=yao_cli`, normalized subcommand name, outcome, failure class, and timestamp. `telemetry-emit` gives Browser/Chrome/IDE/wrapper integrations a stable local hook for appending one normalized external event into `.yao/telemetry_spool/external_events.jsonl`, with dry-run validation and no raw content fields. `telemetry-hooks` renders Browser, Chrome, VS Code, CLI wrapper, and provider-adapter recipes with dry-run commands, import commands, trigger points, and explicit `native_auto_capture=false` caveats. `telemetry_native_host.py` now provides a tested Browser/Chrome Native Messaging host: it reads length-prefixed stdio JSON, rejects raw content fields, writes metadata-only events, and generates an operator-installable launcher plus Chrome host manifest. `telemetry-import` accepts already-sanitized external client JSONL or the emitted spool, validates the whole file before appending, supports dry-run, defaults `source=external`, and rejects any raw content or unknown metadata fields without mutating the local event stream. It still needs an installed real extension or provider client that sends production events to the native host. + +Next move: install a real Browser/Chrome extension or provider client against the native host and record production metadata events, then let Atlas rank stale, drifting, or conflicting skills by real usage impact. + +### 5. Trust report is structural, not full security review + +Trust v0 blocks obvious secrets and remote inline execution, and now makes checksum scope explicit: `package_sha256` is a stable source-contract digest that excludes generated reports, packages, and raw local telemetry; archive integrity is carried by Package Verification and Registry as `archive_sha256`. Registry v0 carries both checksums into distributable metadata. Package Verification v0 now checks generated manifests, target adapters, archive path safety, required source entries, and registry parity. Install Simulation v0 now extracts the generated archive into a temporary local skill root, verifies entrypoint, manifest, interface, reports, and adapters, then checks every packaged target/capability pair against the installed `security/permission_policy.json` approval and `target_enforcement` mapping. Local Install Sync Preflight now runs the same install simulation before syncing source into the disabled or active local install path, so a broken package or permission enforcement gap fails before stale files are removed or new files are copied. Upgrade Check v0 now compares package baselines, recommends semver bumps, and blocks breaking changes without an adequate declared version. Trust Report now distinguishes CLI scripts from declared internal modules, so help-surface warnings focus on executable entrypoints. Network-capable scripts are now covered by `security/network_policy.json`, with `allowed_hosts` checked against HTTPS URL literals. Trust Report also executes `python3 scripts/name.py --help` for CLI scripts with `argparse`, recording pass/fail smoke evidence without executing scripts that lack a help surface. Target adapters now carry `permission_contract` and `target_permission_contract` fields, including network, file-write, subprocess, and interactive capability counts. Governed permission gates now require reviewer-approved scope, reason, expiry, evidence, and target-enforcement mapping in `security/permission_policy.json`. Runtime Permission Probes now verify that packaged adapters expose the target permission contract and explicitly report native-enforcement limits. The remaining trust gap is native runtime permission enforcement in real clients/installers. + +Next move: add real client or installer permission enforcement integration. + +## Current Gate Evidence + +| Gate | Current Result | +| --- | --- | +| Output Eval | `5` cases, DeepSeek provider-backed execution for `10 / 10` variants, with-skill pass rate `43.67`, baseline pass rate `23.67`, delta `20.0`, with file-backed, near-neighbor, boundary coverage, `10` command execution runs, `0` recorded fixture runs, `10` provider model-executed runs in root release evidence, `10` observed token rows, `0` estimated token rows, provider runner v0 available, `5` blind A/B review pairs, a generated `reports/output_review_decisions.json` template, `0 / 5` reviewer decisions pending, `0` answer keys revealed, and `5` pending answers hidden | +| Runtime Conformance | `5 / 5` targets passing | +| Target Compiler | `5 / 5` compiled target contracts generated for OpenAI, Claude, generic, Agent Skills compatible, and VS Code / Copilot outputs, including target permission contracts and target-native behavior contracts | +| Trust | `0` secret findings, `1` pinned dependency file, `69` declared internal modules, `3 / 3` network-capable scripts covered by bounded host policy, `86 / 86` CLI help smoke checks passing across `155` scripts, source-contract hash scope explicit | +| Permission Governance | `3 / 3` required high-permission capabilities approved, `0` missing, `0` invalid, `0` expired | +| Runtime Permission Probes | `4 / 4` target adapters probed, `0` native-enforcement adapters, `4` explicit metadata fallbacks, `4` residual risks retained for reviewer visibility | +| Skill Atlas | `12` scanned skills, `1` actionable root skill, `1` telemetry report, `0` actionable route collisions, `0` actionable owner gaps, `0` actionable stale skills, `0` actionable drift signals, `24` scoped non-actionable issue signals retained for visibility | +| Registry Audit | package metadata generated with version, owner, license, source checksum, archive checksum, Skill IR provenance, and compatibility matrix | +| Package Verification | `4 / 4` target adapters present, archive verified, `580` zip entries, `0` nested SKILL.md entries, `0` failures, `0` warnings | +| Install Simulation | archive with `580` entries extracted into a local verification root, `0` nested SKILL.md entries, entrypoint/manifest/interface loaded, reports present, `4` adapters readable, `12` installer permission checks enforced, `0` permission failures, `0` failures, `0` warnings | +| Local Install Sync Preflight | `make sync-local-install` and `make sync-active-install` rebuild the package first, then sync only after install simulation passes with `12` enforced installer permission checks and `0` permission failures | +| Upgrade Check | current package declares `minor` over the 1.0.0 baseline, recommended bump is `minor`, and release notes include added targets plus checksum changes | +| Adoption Drift | `1` metadata-only review event, `1` adoption sample, adoption `100`, risk band `low`; optional `yao.py` CLI capture, external client `telemetry-emit`, `5` `telemetry-hooks` recipes, Browser/Chrome native messaging host, and validated external JSONL import are available but off by default for reproducible release evidence; raw `reports/telemetry_events.jsonl` is gitignored and blocked from zip packages | +| Review Waivers | ledger generated; current release has `2` warning gates that still need reviewer decision or a time-bounded waiver; blockers remain non-waivable in v0, and world-class evidence gaps must be closed with accepted evidence rather than waived as completed | +| Review Annotations | ledger generated; current release has `0` reviewer annotations and `0` open annotation blockers | +| Review Studio | decision `review`, world-class score `91`, `16` gates, `0` blockers, `3` warnings, `3` review actions, `0` open annotation blockers | +| Skill OS 2.0 Audit | `15` audited areas, with local foundation evidence separated from human-required and external-required gaps | +| World-Class Evidence Plan | `4` remaining evidence tasks: `1` human-required and `3` external-required, all with runbooks, success checks, and privacy contracts | +| World-Class Evidence Ledger | `4` evidence entries remain pending: `1` human-required and `3` external-required; anti-overclaim guards block planned work, metadata fallbacks, pending review, and local command runners from counting as completion | +| Benchmark Reproducibility | local reproducibility ready with `25` required artifacts, `0` missing artifacts, `23` reproduction commands, and `3` disclosed failure cases; human review, native permission enforcement, native telemetry, and provider ledger approval remain explicit limitations | +| IR-first Packaging | `openai`, `claude`, `generic`, and `vscode` adapters include compiler contracts, permission contracts, target-native behavior contracts, IR provenance, semantic parity checks, and install-scope notes where applicable | +| Context Budget | initial load `987/1000`; deferred `530167/120000`; resource governance governed | +| CI | `make ci-test` target count is `83` after adding the dedicated Operator UX verifier | + +## Next Highest-Leverage Moves + +1. Deepen target-native behavior contracts into provider-native execution and provider-native installer integrations. +2. Add native client or installer runtime enforcement for approved high-permission capabilities. +3. Complete independent provider ledger review, then record the current blind A/B decisions before claiming fully ready status. +4. Add real reviewer annotation records during the next human review pass. +5. Install a real Browser/Chrome extension or provider client against `telemetry_native_host.py` and record production metadata events. diff --git a/reports/skill-overview-v2-plan.md b/reports/skill-overview-v2-plan.md new file mode 100644 index 0000000..072bc11 --- /dev/null +++ b/reports/skill-overview-v2-plan.md @@ -0,0 +1,489 @@ +# Skill Overview v2 报告升级方案 + +## 结论 + +建议把 `reports/skill-overview.html` 从“Skill 概览页”升级为“Skill 生成审计报告”。它不只说明新 Skill 是什么,还要解释它为什么这样设计、质量如何、风险在哪里、后续怎么升级。 + +本版方案在上一版基础上做了三点修正: + +1. 在最前面新增“技能概述”模块,让用户先读懂这个 Skill 的一句话定位、核心价值、适用对象和交付结果,再看指标和图表。 +2. 将“总览指标”后移为第二模块,避免用户一打开页面就看到抽象分数,却还不知道这个 Skill 是什么。 +3. 将报告定位从“好看的说明页”收紧为“可解释、可评估、可复盘的生成报告”,所有图表和分数都必须能从本地 Skill 文件或已有 reports 推导出来。 + +## 目标 + +每次创建一个全新 Skill 后,同时生成一份双语 HTML 报告: + +- 默认中文简体,右上角可以切换英文。 +- 白色背景,沿用 Kami 的长文档排版节奏。 +- 顶部吸顶导航,每个一级模块保持四个汉字。 +- 模块足够细,既能让普通用户理解 Skill,也能让维护者评估质量。 +- 包含图表、矩阵、数字化指标和后续升级建议。 +- 静态 HTML 可离线打开,不依赖外部图表库或在线资源。 + +## 非目标 + +- 不把报告做成复杂 Web App。 +- 不引入外部 JS 图表库。 +- 不让报告替代 `SKILL.md`、`README.md`、`agents/interface.yaml` 等源文件。 +- 不用模型主观打分冒充事实评分;所有分数必须可解释。 +- 不在报告里暴露用户私密原文,除非这些内容已经存在于 Skill 包体内且适合展示。 + +## 报告模块 + +顶部导航建议保留 9 个一级模块,全部四字: + +1. 技能概述 +2. 总览指标 +3. 能力画像 +4. 原理结构 +5. 契约边界 +6. 质量评估 +7. 风险治理 +8. 包体资产 +9. 迭代路线 + +### 1. 技能概述 + +作用:让用户先用一分钟读懂这个新 Skill。 + +内容: + +- Skill 名称、显示名称、创建时间、成熟度。 +- 一句话定位:这个 Skill 主要解决什么重复任务。 +- 核心价值:它能帮用户少做什么、稳定什么、交付什么。 +- 适用对象:个人使用、团队复用、平台迁移或库级能力。 +- 交付结果:最终生成哪些文件和报告。 +- 使用提醒:最应该打开的文件和下一步建议。 + +图表: + +- “Skill 名片”信息块。 +- “从输入到交付”的三段式流程条:输入材料 -> Skill 包体 -> 可复用能力。 + +数据来源: + +- `SKILL.md` frontmatter +- `README.md` +- `manifest.json` +- `agents/interface.yaml` +- `reports/intent-context.json` +- `reports/skill-overview.json` + +### 2. 总览指标 + +作用:用数字化方式快速判断这个 Skill 的当前状态。 + +内容: + +- 完整度分数 +- 触发清晰度分数 +- 证据充分度分数 +- 可维护性分数 +- 可迁移性分数 +- 上下文成本估算 + +图表: + +- 5 维雷达图。 +- 指标卡片。 +- “当前成熟度”状态条。 + +评分原则: + +- 分数必须来自结构化规则,而不是主观判断。 +- 每个分数旁边必须显示 1 到 3 条原因。 +- 缺失数据时显示“未生成 / 证据不足”,而不是编造结论。 + +### 3. 能力画像 + +作用:说明这个 Skill 在能力地图中的位置。 + +内容: + +- 它属于创建型、评审型、执行型、研究型、写作型还是工具型 Skill。 +- 使用模式:Scaffold / Production / Library。 +- 触发强度:显式调用、自然语言匹配、上下文辅助。 +- 复用范围:个人、团队、跨项目、跨平台。 + +图表: + +- 能力定位矩阵:横轴为“执行确定性”,纵轴为“知识密度”。 +- 使用对象标签组。 + +数据来源: + +- `manifest.json` +- `agents/interface.yaml` +- `reports/prompt-quality-profile.json` +- `reports/system-model.json` + +### 4. 原理结构 + +作用:解释这个 Skill 为什么这样组织。 + +内容: + +- 意图澄清 +- 边界路由 +- 资产分层 +- 证据回路 +- 漂移观察 +- 杠杆升级 + +图表: + +- 闭环流程图。 +- 分层架构图:入口层、参考层、脚本层、评估层、报告层。 +- 如果存在 system model,展示反馈回路。 + +数据来源: + +- `SKILL.md` +- `references/` +- `scripts/` +- `evals/` +- `reports/system-model.json` + +### 5. 契约边界 + +作用:说明什么时候该触发,输入输出是什么,边界在哪里。 + +内容: + +- 触发描述 +- 应触发场景 +- 不应触发场景 +- 输入材料 +- 输出结果 +- 排除项 +- 失败或缺资料时的处理方式 + +图表: + +- 输入输出矩阵。 +- should-trigger / should-not-trigger 对照表。 +- 边界卡片:Owned / Adjacent / Excluded。 + +数据来源: + +- `SKILL.md` frontmatter +- `reports/intent-dialogue.json` +- `reports/intent-confidence.json` +- `evals/` +- `agents/interface.yaml` + +### 6. 质量评估 + +作用:评估这个 Skill 是否可靠、清晰、可维护。 + +内容: + +- `description` 是否清楚。 +- `SKILL.md` 是否过重。 +- `references/` 是否承担了长指导。 +- `scripts/` 是否承担了确定性逻辑。 +- `evals/` 是否覆盖触发和质量。 +- prompt quality、artifact design、system model 摘要。 + +图表: + +- 条形评分图。 +- 质量证据表。 +- “强项 / 缺口 / 建议”三列分析。 + +数据来源: + +- `reports/prompt-quality-profile.json` +- `reports/artifact-design-profile.json` +- `reports/system-model.json` +- `reports/output-risk-profile.json` +- 包体文件统计 + +### 7. 风险治理 + +作用:让用户看到这个 Skill 当前最可能失败在哪里。 + +内容: + +- 误触发风险 +- 输出漂移风险 +- 证据不足风险 +- 包体膨胀风险 +- 跨平台迁移风险 +- 人工判断边界 + +图表: + +- 风险热力图:影响程度 x 发生概率。 +- 风险清单:风险、信号、应对方式、证据来源。 + +数据来源: + +- `reports/output-risk-profile.json` +- `reports/system-model.json` +- `reports/intent-confidence.json` +- `reports/portability_score.json` + +### 8. 包体资产 + +作用:让 reviewer 快速确认这个 Skill 的文件结构是否合理。 + +内容: + +- `SKILL.md` +- `README.md` +- `agents/interface.yaml` +- `manifest.json` +- `references/` +- `scripts/` +- `evals/` +- `reports/` + +图表: + +- 包体树状图。 +- 文件类型分布 donut。 +- 资产状态表:存在、缺失、可选、过重、建议补充。 + +数据来源: + +- 本地文件系统扫描 +- `manifest.json` +- `agents/interface.yaml` + +### 9. 迭代路线 + +作用:把下一步升级方向讲清楚,避免无序扩张。 + +内容: + +- Top 3 下一步升级方向。 +- 每个方向的原因、动作、收益和暂缓项。 +- 哪些内容现在不应该做。 +- 哪些前置条件满足后再升级。 + +图表: + +- 优先级矩阵:价值 x 成本。 +- 迭代时间线。 + +数据来源: + +- `reports/iteration-directions.json` +- `reports/system-model.json` +- `reports/intent-confidence.json` + +## 指标设计 + +### completeness_score + +衡量关键资产是否齐全。 + +参考规则: + +- `SKILL.md` 存在:基础分 +- `agents/interface.yaml` 存在:加分 +- `README.md` 存在:加分 +- `manifest.json` 存在:加分 +- `references/`、`scripts/`、`evals/`、`reports/` 根据成熟度判断是否加分 + +### trigger_score + +衡量触发是否清晰。 + +参考规则: + +- frontmatter `description` 是否存在 +- 是否说明任务、输入、输出和非目标 +- 是否有 should-trigger / should-not-trigger 样例 +- 是否有 route eval + +### evidence_score + +衡量报告证据是否充分。 + +参考规则: + +- 是否有 intent 报告 +- 是否有 artifact design 报告 +- 是否有 prompt quality 报告 +- 是否有 system model 报告 +- 是否有 iteration directions 报告 + +### maintainability_score + +衡量是否容易维护。 + +参考规则: + +- `SKILL.md` 不过长 +- 长指导进入 `references/` +- 确定性逻辑进入 `scripts/` +- 测试或 eval 可随包体迁移 +- 报告和 manifest 保持同步 + +### portability_score + +衡量跨环境复用能力。 + +参考规则: + +- `agents/interface.yaml` 是否完整 +- `manifest.json` 是否声明目标平台 +- 是否有降级策略 +- 是否避免硬编码私有路径 + +### context_cost + +估算上下文成本。 + +参考规则: + +- `SKILL.md` 字数 +- references 文件数量和总字数 +- reports 是否只在需要时读取 +- 是否把长说明放到入口文件 + +## 数据模型建议 + +建议生成一个中间报告模型: + +```text +reports/skill-overview.json +``` + +核心结构: + +```json +{ + "skill_summary": {}, + "scorecard": {}, + "capability_profile": {}, + "principle_model": {}, + "contract_boundary": {}, + "quality_review": {}, + "risk_governance": {}, + "package_assets": {}, + "iteration_roadmap": {}, + "report_contract": {} +} +``` + +HTML 只负责渲染这个模型,不直接到处读取文件。 + +## 实现拆分 + +建议把现有报告渲染器拆成四层: + +```text +Skill package + -> report model builder + -> metrics calculator + -> chart renderer + -> HTML renderer +``` + +建议文件: + +- `scripts/render_skill_overview.py`:保留 CLI 入口和编排。 +- `scripts/skill_report_model.py`:汇总 Skill 数据和各模块内容。 +- `scripts/skill_report_metrics.py`:计算分数和原因。 +- `scripts/skill_report_charts.py`:生成内联 SVG 图表。 +- `tests/verify_skill_overview.py`:验收 HTML 主结构。 +- `tests/verify_skill_report_metrics.py`:验收分数规则。 +- `tests/verify_skill_report_charts.py`:验收 SVG 图表和无外部依赖。 + +## 排版约束 + +- 背景保持纯白。 +- 使用 ink-blue 作为强调色。 +- 采用 Kami 式长文档节奏:先结论,再证据,再解释。 +- 图表必须有 caption,caption 要说洞察,不只说图表名称。 +- 不使用外部字体、外部图片或外部图表库。 +- 顶部导航吸顶。 +- 一级菜单全部四字。 +- 默认中文简体,英文作为切换视图。 +- 每个模块要能独立扫描,不依赖读者从头读到尾。 + +## 生成完成后的用户文案 + +创建 Skill 后,CLI / 输出摘要建议显示: + +```text +Skill 已创建完成。 + +建议先打开总结报告: +/reports/skill-overview.html + +这份报告默认使用中文简体,右上角可切换英文。它会展示这个 Skill 的概述、指标、原理、触发边界、输入输出、质量评估、风险治理、包体资产和后续升级路线。 +``` + +## 验收标准 + +功能验收: + +- 新建 Skill 后自动生成 `reports/skill-overview.html` 和 `reports/skill-overview.json`。 +- HTML 默认中文简体。 +- 右上角可以切换英文。 +- 顶部导航吸顶,一级菜单均为四字。 +- 页面至少包含 9 个一级模块。 +- 页面至少包含 4 类 SVG 图表:雷达图、流程图、风险热力图、资产分布图。 +- 每个数字分数都有原因说明。 +- 缺失数据时显示证据不足,不编造结论。 + +测试验收: + +- `python3 tests/verify_skill_overview.py` +- `python3 tests/verify_yao_cli.py` +- `python3 tests/verify_skill_report_metrics.py` +- `python3 tests/verify_skill_report_charts.py` +- `make ci-test` + +视觉验收: + +- 桌面端顶部导航不遮挡正文。 +- 移动端导航可以换行或紧凑展示,不溢出。 +- 图表在白底上清晰可读。 +- 中文默认视图没有大段英文占位。 +- 报告可直接用浏览器打开。 + +## 风险与应对 + +### 风险一:报告变得过重 + +应对:模块多,但数据来源要轻。没有证据的模块显示“暂无证据”,不要额外生成大量报告。 + +### 风险二:分数看起来像主观评价 + +应对:每个分数绑定规则和原因,报告展示“为什么得这个分”。 + +### 风险三:渲染器继续膨胀 + +应对:先拆 model、metrics、charts,再扩 HTML。不要继续把所有逻辑塞进 `render_skill_overview.py`。 + +### 风险四:英文切换质量不足 + +应对:默认中文必须完整;英文可以先作为解释层,不要求每个用户原文都翻译,但不能影响中文体验。 + +## 推荐实施顺序 + +1. 定义 v2 `skill-overview.json` 数据结构。 +2. 新增 metrics 计算器,先产出 scorecard 和原因。 +3. 新增 chart renderer,生成内联 SVG。 +4. 重构 HTML 为 9 个一级模块。 +5. 更新 `init_skill.py` 和 `yao.py` 的报告输出文案。 +6. 生成示例 Skill 报告,做视觉检查。 +7. 跑专项测试和完整 CI。 + +## 最小可行版本 + +如果希望先小步迭代,第一版只做: + +- 新增“技能概述”模块。 +- 新增“总览指标”模块。 +- 新增 5 维雷达图。 +- 新增分数原因说明。 +- 保留现有其他模块结构。 + +这能最快提升报告的第一屏价值,同时为后续 9 模块完整版本铺好数据结构。 diff --git a/reports/skill-overview.html b/reports/skill-overview.html new file mode 100644 index 0000000..c5a0f6d --- /dev/null +++ b/reports/skill-overview.html @@ -0,0 +1,1005 @@ + + + + + + yao-meta-skill Skill 生成审计报告 + + + + +
    + + +
    +
    +
    +
    +
    +

    YAO Skill 生成审计报告YAO Skill Generation Audit

    +

    yao-meta-skill

    +

    技能审计报告Yao Meta Skill Audit Report

    +

    这份报告默认使用中文简体,把新 Skill 的定位、指标、原理、契约、质量、风险、资产和迭代路线整理为一份可审计的 HTML 报告。Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.

    +
    技能名称:yao-meta-skillSkill name: yao-meta-skill成熟度:治理Maturity: governed格式:Agent SkillsFormat: agent-skills更新时间:2026-03-31Updated: 2026-03-31
    +
    openaiopenaiclaudeclaudegenericgenericvscodevscode
    +
    + +
    +
    完整度Completeness100SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
    触发清晰Trigger clarity100frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
    证据充分Evidence depth100已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.
    上下文成本Context cost42入口约 314 个词/字,references 约 16934 个词/字。Entrypoint is about 314 words/characters; references are about 16934.
    +
    + +
    +
    +
    +

    技能概述Overview

    +

    先用一屏说明这个 Skill 是什么、给谁用、交付什么。A first-screen explanation of what this skill is, who it serves, and what it delivers.

    +
    +
    +
    +

    作用定位Role

    +
    • Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.
    • 把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。Turn one-off experience into a reusable, evaluable, and portable skill package.
    • 交付结果:SKILL.md, agents/interface.yaml, skill-ir/examples/yao-meta-skill.json, reports/compiled_targets.md, reports/output_quality_scorecard.md, reports/output_execution_runs.md, reports/output_blind_review_pack.md, reports/output_review_kit.md, reports/output_review_adjudication.md, reports/benchmark_reproducibility.md, reports/output_blind_answer_key.json, reports/conformance_matrix.md, reports/security_trust_report.md, reports/runtime_permission_probes.md, reports/skill_atlas.html, reports/registry_audit.md, reports/package_verification.md, reports/install_simulation.md, reports/upgrade_check.md, reports/adoption_drift_report.md, reports/review_waivers.md, reports/world_class_evidence_plan.md, reports/world_class_evidence_ledger.md, reports/review_annotations.md, reports/review-studio.html, reports/skill-interpretation.html, reports/skill-overview.htmlDeliverables: SKILL.md, agents/interface.yaml, skill-ir/examples/yao-meta-skill.json, reports/compiled_targets.md, reports/output_quality_scorecard.md, reports/output_execution_runs.md, reports/output_blind_review_pack.md, reports/output_review_kit.md, reports/output_review_adjudication.md, reports/benchmark_reproducibility.md, reports/output_blind_answer_key.json, reports/conformance_matrix.md, reports/security_trust_report.md, reports/runtime_permission_probes.md, reports/skill_atlas.html, reports/registry_audit.md, reports/package_verification.md, reports/install_simulation.md, reports/upgrade_check.md, reports/adoption_drift_report.md, reports/review_waivers.md, reports/world_class_evidence_plan.md, reports/world_class_evidence_ledger.md, reports/review_annotations.md, reports/review-studio.html, reports/skill-interpretation.html, reports/skill-overview.html
    +
    +
    交付流程Delivery Flow输入材料Input materialSkill 包体Skill package可复用能力Reusable capability
    交付流程把用户输入、生成的包体和可复用能力放在一条线上。The delivery flow places user input, generated package, and reusable capability on one path.
    +
    +
    +
    + +
    +
    +
    +

    总览指标Metrics

    +

    分数来自本地文件和 reports 证据,缺失时明确标为证据不足。Scores are derived from package files and reports; missing inputs are shown as evidence gaps.

    +
    +
    +

    指标判读Reading

    +

    先看雷达图判断能力短板,再看每项分数的证据原因。分数不是装饰数字,必须和本地文件、reports 证据或证据不足提示对应。Read the radar first for weak spots, then inspect each score with its evidence. Scores must map to local files, reports, or explicit evidence gaps.

    +
    +
    +
    +
    +
    +
    评分雷达Rating Radar完整度Completeness触发清晰Trigger clarity证据充分Evidence depth可维护性Maintainability可迁移性Portability
    评分雷达展示结构完整度、触发边界、证据、维护和迁移的相对强弱。The radar chart compares completeness, trigger clarity, evidence, maintainability, and portability.
    +
    +

    成熟度条Maturity Bar

    +

    这里把每个指标压缩为状态、分数和第一条证据,先给出整体判断,再进入下方明细。This compresses each metric into status, score, and the first evidence item before the detailed cards below.

    +
    1. 稳定Stable完整度Completeness100SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
    2. 稳定Stable触发清晰Trigger clarity100frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
    3. 稳定Stable证据充分Evidence depth100已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.
    4. 稳定Stable可维护性Maintainability100SKILL.md 约 314 个词/字。SKILL.md is about 314 words/characters.
    5. 稳定Stable可迁移性Portability100agents/interface.yaml 已存在。agents/interface.yaml exists.
    6. 关注Watch上下文成本Context cost42入口约 314 个词/字,references 约 16934 个词/字。Entrypoint is about 314 words/characters; references are about 16934.
    +
    +
    +
    +
    指标明细Metric Details
    +
    完整度Completeness100
    • SKILL.md 已存在,是 Skill 的入口。SKILL.md exists and acts as the skill entrypoint.
    • README.md 已存在,便于人工阅读。README.md exists for human-readable usage.
    • agents/interface.yaml 已存在,便于跨平台适配。agents/interface.yaml exists for cross-platform adaptation.
    触发清晰Trigger clarity100
    • frontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.
    • description 有足够长度说明任务边界。The description is long enough to explain the task boundary.
    • description 已包含使用场景或排除边界信号。The description includes usage-scenario or exclusion-boundary signals.
    证据充分Evidence depth100
    • 已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.
    • skill-ir.json 已存在。skill-ir.json exists.
    • compiled_targets.json 已存在。compiled_targets.json exists.
    可维护性Maintainability100
    • SKILL.md 约 314 个词/字。SKILL.md is about 314 words/characters.
    • 入口文件保持克制,可维护性较好。The entrypoint stays restrained, which supports maintainability.
    • references/ 已承载扩展指导。references/ carries extended guidance.
    可迁移性Portability100
    • agents/interface.yaml 已存在。agents/interface.yaml exists.
    • manifest.json 已存在。manifest.json exists.
    • 目标平台或 adapter target 已声明。Target platforms or adapter targets are declared.
    上下文成本Context cost42
    • 入口约 314 个词/字,references 约 16934 个词/字。Entrypoint is about 314 words/characters; references are about 16934.
    • 分数越高代表上下文成本越低。A higher score means lower context cost.
    • 上下文成本偏高,建议压缩入口或拆分 references。Context cost is high; compress the entrypoint or split references further.
    +
    +
    +
    +
    + +
    +
    +
    +

    能力画像Capability

    +

    判断这个 Skill 在能力地图中的位置和复用范围。Places this skill on a capability map and clarifies reuse scope.

    +
    +
    +
    能力矩阵Capability Matrix执行确定性Execution certainty知识密度Knowledge densityExecution operationExecution operation
    能力矩阵说明这个 Skill 更偏知识密集还是执行确定。The capability matrix shows whether the skill leans toward knowledge density or execution certainty.
    +
    +

    画像摘要Profile

    +
    • 能力类型:Execution operationCapability type: Execution operation
    • 成熟度:governedMaturity: governed
    • 触发强度:手动触发 + description 路由Trigger strength: Manual activation plus description-based routing
    • 复用范围:跨平台Reuse scope: Cross-platform
    +
    +
    +
    +
    + +
    +
    +
    +

    原理结构Principle

    +

    说明入口、参考、脚本、评估和报告如何组成一个稳定闭环。Explains how entrypoint, references, scripts, evals, and reports form a stable loop.

    +
    +
    +
    分层结构Layered Structure入口层Entrypoint layer参考层Reference layer脚本层Script layer评估层Evaluation layer报告层Report layer
    分层结构展示入口、参考、脚本、评估和报告如何各司其职。The layered structure shows how entrypoint, references, scripts, evals, and reports each carry a distinct role.
    +
    +
    +

    执行流程Execution Flow

    +
    1. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.
    2. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Capture job, output, exclusions, constraints, standards, lightest fit.
    3. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.
    4. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.
    5. 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned.
    +
    +
    +

    调用方式How To Use

    +
    1. 当你需要把工作流或笔记沉淀成结构精简、触发清晰且带必要 eval 的可复用 Skill 时使用 $yao-meta-skill。Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.
    2. Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.Use this skill when the request matches the frontmatter description.
    +
    +
    +
    +
    +
    + +
    +
    +
    +

    契约边界Contract

    +

    把触发、输入、输出和排除场景放在同一屏。Keeps trigger, inputs, outputs, and exclusions on the same screen.

    +
    +
    +
    +

    触发描述Trigger

    +

    Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording.

    +

    输入材料Inputs

    +
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。an existing skill directory that needs refactor, evaluation, packaging, or governance hardening
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。benchmark references, local constraints, desired maturity tier, and review standards
    +
    +
    +

    输出结果Outputs

    +
    • 原始说明可切换到英文查看;默认中文报告保留结论与结构说明。A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.
    • 结构化 Skill 目录,共 8 类关键资产。Structured skill directory with 8 key asset groups.
    +

    不应触发Should Not Trigger

    +
    • 只需要一次性回答、没有复用价值的临时请求。One-off requests that do not need reusable skill behavior.
    • 要求直接执行相邻任务,而不是沉淀或使用这个 Skill。Requests to perform an adjacent task directly rather than create or use this skill.
    • 缺少必要事实且用户不允许澄清的场景。Cases that lack required facts and do not allow clarification.
    +
    +
    +
    +
    + +
    +
    +
    +

    质量评估Quality

    +

    展示强项、缺口和建议,避免只给分不解释。Shows strengths, gaps, and recommendations instead of scores without explanation.

    +
    +
    + + + +
    类型Type证据Evidence建议Action
    强项Strength触发面保持精简,并锚定在 frontmatter description。The trigger surface stays lean and anchored in the frontmatter description.保留并复用Keep
    强项Strength已生成 Skill IR,核心语义可先于平台打包被审查和迁移。Skill IR is generated so core semantics can be reviewed and migrated before platform packaging.保留并复用Keep
    强项Strength已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。Target compilation evidence is generated to review how IR maps to OpenAI, Claude, generic, and other target contracts.保留并复用Keep
    缺口Gap上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。Context cost needs improvement: Entrypoint is about 314 words/characters; references are about 16934.纳入下一轮修复Fix next
    +
    +
    +

    执行证据Execution Evidence

    +
    • 变体运行:10Variant runs: 10
    • 模型执行:10Model executions: 10
    • 记录样本:0Recorded fixtures: 0
    • Token 估算:0Token estimates: 0
    +
    +
    +

    盲评审定Blind Adjudication

    +
    • 评审进度:0 / 5Review progress: 0 / 5
    • 待评审:5Pending review: 5
    • 一致率:暂无Agreement rate: not available yet
    • 非法决策:0Invalid decisions: 0
    +
    +
    +
    +
    +

    评审原则Review Rule

    +
    • 先记录 reviewer 对 A/B 的选择,再打开答案 key 计算一致率。Record the reviewer's A/B choice before opening the answer key and calculating agreement.
    • 缺少真实 reviewer 决策时只显示待评审,不伪造人工结论。When real reviewer decisions are missing, show pending status instead of fabricating human conclusions.
    +
    +
    +

    运行原则Run Rule

    +
    • recorded fixture 只能证明可复现样本,不等同于模型执行。A recorded fixture proves reproducible samples only; it is not model execution.
    • 只有 provider runner 返回 model metadata 时才计入 model-executed。Only provider runners that return model metadata count as model-executed.
    +
    +
    +
    +
    +
    + +
    +
    +
    +

    风险治理Risk

    +

    提前暴露误触发、漂移、证据不足和迁移风险。Surfaces trigger, drift, evidence, and portability risks before the package grows.

    +
    +
    +
    风险热力Risk Heatmap000200300发生概率Probability影响程度Impact
    风险热力图用影响程度和发生概率标出当前治理重点。The risk heatmap marks governance priorities by impact and probability.
    +
    + + + +
    风险Risk信号Signal应对Response
    误触发风险Trigger riskfrontmatter description 已存在,具备基础路由面。The frontmatter description exists, giving the skill a basic routing surface.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    输出漂移风险Output drift risk已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    证据不足风险Evidence gap risk已生成 20 / 20 类报告证据。Generated 20 / 20 evidence report types.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    包体膨胀风险Package bloat riskSKILL.md 约 314 个词/字。SKILL.md is about 314 words/characters.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    跨平台迁移风险Portability riskagents/interface.yaml 已存在。agents/interface.yaml exists.先补证据和边界,再增加包体复杂度。Improve evidence and boundaries before adding package complexity.
    +
    +
    +

    世界证据World Evidence

    世界级证据尚未完成:4 项待补,0 项已接受。World-class evidence is not complete: 4 pending, 0 accepted.

    证据待补Evidence pending
    待补证据Pending4仍需外部或人工证据接受。External or human evidence still needs acceptance.
    已接受Accepted0已通过 source check 与提交契约。Passed source checks and submission contract.
    源检查Source Checks12 / 19通过数 / 总检查数。Passed checks / total checks.
    外部证据External evidence

    提供商留出Provider Holdout

    缺少真实 provider 模型运行和 token metadata。Missing a real provider model run and token metadata.

    阻塞检查Blocked Checks

    当前没有阻塞检查。No blocked checks right now.

    人工证据Human evidence

    人工盲评Human Adjudication

    盲评 pair 仍待真实 reviewer 决策。Blind-review pairs still need real reviewer decisions.

    阻塞检查Blocked Checks
    • 无待判定No pending decisions
    • 盲评完成Judgments complete
    • Reviewer metadataReviewer metadata
    外部证据External evidence

    原生权限Native Permission

    原生 runtime enforcement 仍待目标客户端或外部安装器证明。Native runtime enforcement still needs target-client or external-installer proof.

    阻塞检查Blocked Checks
    • 原生执行Native enforcement
    外部证据External evidence

    原生遥测Native Telemetry

    真实外部客户端 metadata-only 事件仍未导入。Real external-client metadata-only events have not been imported yet.

    阻塞检查Blocked Checks
    • 外部事件External events
    +
    +
    + +
    +
    +
    +

    包体资产Assets

    +

    让 reviewer 快速确认关键文件、目录和资产分布。Lets reviewers confirm key files, directories, and asset distribution quickly.

    +
    +
    +
    资产分布Asset Distribution393项393 itemsSKILL.mdSKILL.mdREADME.mdREADME.mdagents/interface.yamlagents/interface.yamlmanifest.jsonmanifest.jsonreferencesreferencesscriptsscripts
    资产分布图展示当前包体的文件和目录重心。The asset distribution chart shows where files and directories are concentrated.
    + + + +
    路径Path作用Role类型Type
    SKILL.mdSkill 入口文件Skill entrypoint文件file
    README.md人类可读使用说明Human-readable usage guide文件file
    agents/interface.yaml跨平台接口元数据Neutral interface metadata文件file
    manifest.json生命周期与打包元数据Lifecycle and portability metadata文件file
    references扩展指导与复用资料Extended guidance and reusable notes目录folder
    scripts确定性脚本或本地工具Deterministic helpers or local tooling目录folder
    evals触发与质量检查Trigger and quality checks目录folder
    reports生成的证据与总结报告Generated evidence and overview artifacts目录folder
    +
    +
    +
    + +
    +
    +
    +

    迭代路线Roadmap

    +

    把下一步升级收束为少数高价值动作。Keeps next iteration moves focused and actionable.

    +
    +
    +
    迭代时间Iteration Timeline下一步 1Next 1补齐世界证据Close world-class evide...下一步 2Next 2Borrow one proven…Borrow one proven…下一步 3Next 3Harden portabilit…Harden portabilit…
    迭代时间线把下一步升级收束成少数可执行动作。The iteration timeline narrows the next upgrade into a few executable moves.
    +
    下一步 1Next 1

    补齐世界证据Close world-class evidence

    世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。World-class evidence still has 4 pending item(s); public completion claims must stay blocked.

    • 补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。Close provider holdout evidence: Missing a real provider model run and token metadata.
    • 补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。Close human adjudication evidence: Blind-review pairs still need real reviewer decisions.
    • 按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。Close all 4 pending evidence item(s) in the ledger (external 3, human 1; 2 not shown here) and keep the claim guard pending.

    全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。Only after the ledger accepts all external and human evidence should the public world-class claim move to review.

    下一步 2Next 2

    有选择地借鉴一个成熟模式Borrow one proven pattern on purpose

    已经有公开 benchmark 对象,下一步应主动选择一个模式借鉴,而不是松散吸收所有做法。You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.

    • 阅读 `obra/superpowers` 中最值得借鉴的模式。Read the strongest pattern from obra/superpowers.
    • 先判断要借鉴的是方法、结构、执行方式还是可迁移性,并且第一轮只借鉴其中一个。Decide whether to borrow method, structure, execution, or portability, but only one of them first.
    • 记录本轮不借鉴的内容,避免包体过重。Record what you will not borrow so the package stays light.

    包体形态更清晰,也减少偶然过度设计。A cleaner package shape with less accidental over-design.

    下一步 3Next 3

    加固跨环境语义Harden portability semantics

    这个 Skill 已经面向跨环境复用,因此早期就需要把契约语义说清楚。The skill already signals reuse across environments, so contract clarity matters early.

    • 确认激活模式、执行上下文和信任假设。Confirm activation mode, execution context, and trust assumptions.
    • 补充或复核非原生目标端的降级策略。Add or review degradation strategy for non-native targets.
    • 至少打包一次 Skill,用来验证 adapter 预期。Package the skill once to verify adapter expectations.

    跨环境复用更安全,目标漂移更少。Safer cross-environment reuse with less target drift.

    +
    +
    +
    +
    + + + diff --git a/reports/skill-overview.json b/reports/skill-overview.json new file mode 100644 index 0000000..bea4304 --- /dev/null +++ b/reports/skill-overview.json @@ -0,0 +1,2266 @@ +{ + "skill_summary": { + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "maturity": "governed", + "updated_at": "2026-03-31", + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": [ + "SKILL.md", + "agents/interface.yaml", + "skill-ir/examples/yao-meta-skill.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_adjudication.md", + "reports/benchmark_reproducibility.md", + "reports/output_blind_answer_key.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/world_class_evidence_plan.md", + "reports/world_class_evidence_ledger.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-interpretation.html", + "reports/skill-overview.html" + ], + "flow": [ + "输入材料", + "Skill 包体", + "可复用能力" + ] + }, + "scorecard": { + "completeness_score": { + "label": "完整度", + "score": 100, + "reasons": [ + "SKILL.md 已存在,是 Skill 的入口。", + "README.md 已存在,便于人工阅读。", + "agents/interface.yaml 已存在,便于跨平台适配。", + "manifest.json 已存在,生命周期信息可追踪。", + "reports/ 已存在,生成证据可以随包体迁移。" + ] + }, + "trigger_score": { + "label": "触发清晰", + "score": 100, + "reasons": [ + "frontmatter description 已存在,具备基础路由面。", + "description 有足够长度说明任务边界。", + "description 已包含使用场景或排除边界信号。", + "evals/ 已存在,可承载触发样例或质量检查。", + "intent-confidence 报告已生成,可辅助判断触发稳定性。" + ] + }, + "evidence_score": { + "label": "证据充分", + "score": 100, + "reasons": [ + "已生成 20 / 20 类报告证据。", + "skill-ir.json 已存在。", + "compiled_targets.json 已存在。", + "intent-dialogue.json 已存在。" + ] + }, + "maintainability_score": { + "label": "可维护性", + "score": 100, + "reasons": [ + "SKILL.md 约 314 个词/字。", + "入口文件保持克制,可维护性较好。", + "references/ 已承载扩展指导。", + "scripts/ 已承载确定性逻辑。", + "evals/ 已承载可迁移检查。" + ] + }, + "portability_score": { + "label": "可迁移性", + "score": 100, + "reasons": [ + "agents/interface.yaml 已存在。", + "manifest.json 已存在。", + "目标平台或 adapter target 已声明。", + "入口文件未发现明显私有绝对路径。" + ] + }, + "context_cost": { + "label": "上下文成本", + "score": 42, + "reasons": [ + "入口约 314 个词/字,references 约 16934 个词/字。", + "分数越高代表上下文成本越低。", + "上下文成本偏高,建议压缩入口或拆分 references。" + ] + } + }, + "capability_profile": { + "archetype": "governed", + "task_family": "Execution operation", + "maturity": "governed", + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台", + "matrix": { + "execution_certainty": 72, + "knowledge_density": 80 + } + }, + "principle_model": { + "nodes": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "layers": [ + "入口层", + "参考层", + "脚本层", + "评估层", + "报告层" + ] + }, + "contract_boundary": { + "trigger": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ], + "should_trigger": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。" + ], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。" + ], + "boundary_cards": [ + { + "label": "Owned", + "body": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + }, + { + "label": "Adjacent", + "body": "相邻任务需要先确认是否应转为独立 Skill。" + }, + { + "label": "Excluded", + "body": "不替代人工事实核查,也不静默扩大职责。" + } + ] + }, + "quality_review": { + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "gaps": [ + "上下文成本需要补强:入口约 314 个词/字,references 约 16934 个词/字。" + ], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。" + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + } + }, + "risk_governance": { + "risks": [ + { + "name": "误触发风险", + "impact": 3, + "probability": 1, + "signal": "frontmatter description 已存在,具备基础路由面。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "输出漂移风险", + "impact": 2, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "证据不足风险", + "impact": 3, + "probability": 1, + "signal": "已生成 20 / 20 类报告证据。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "包体膨胀风险", + "impact": 2, + "probability": 1, + "signal": "SKILL.md 约 314 个词/字。", + "response": "先补证据和边界,再增加包体复杂度。" + }, + { + "name": "跨平台迁移风险", + "impact": 3, + "probability": 1, + "signal": "agents/interface.yaml 已存在。", + "response": "先补证据和边界,再增加包体复杂度。" + } + ], + "risk_families": [ + { + "key": "markdown_readability", + "label": "Markdown readability", + "matched_keywords": [ + "md", + "report", + "doc" + ], + "score": 3, + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability." + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome." + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards." + ] + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "matched_keywords": [ + "source", + "reference" + ], + "score": 2, + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself." + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote." + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note." + ] + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "matched_keywords": [ + "capture" + ], + "score": 1, + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region." + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists." + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state." + ] + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "matched_keywords": [ + "script" + ], + "score": 1, + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered." + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code." + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode." + ] + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "matched_keywords": [ + "summary" + ], + "score": 1, + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints." + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input." + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged." + ] + } + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ] + }, + "world_class_readiness": { + "ready": false, + "decision": "evidence-pending", + "entry_count": 4, + "pending_count": 4, + "accepted_count": 0, + "external_pending_count": 3, + "human_pending_count": 1, + "source_check_count": 19, + "source_pass_count": 12, + "conclusion_zh": "世界级证据尚未完成:4 项待补,0 项已接受。", + "conclusion_en": "World-class evidence is not complete: 4 pending, 0 accepted.", + "entries": [ + { + "key": "provider-holdout", + "label_zh": "提供商留出", + "label_en": "Provider Holdout", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "缺少真实 provider 模型运行和 token metadata。", + "summary_en": "Missing a real provider model run and token metadata.", + "blocked_checks": [] + }, + { + "key": "human-adjudication", + "label_zh": "人工盲评", + "label_en": "Human Adjudication", + "category": "human", + "category_zh": "人工证据", + "category_en": "Human evidence", + "status": "pending", + "summary_zh": "盲评 pair 仍待真实 reviewer 决策。", + "summary_en": "Blind-review pairs still need real reviewer decisions.", + "blocked_checks": [ + "No pending decisions", + "Judgments complete", + "Reviewer metadata" + ] + }, + { + "key": "native-permission-enforcement", + "label_zh": "原生权限", + "label_en": "Native Permission", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "原生 runtime enforcement 仍待目标客户端或外部安装器证明。", + "summary_en": "Native runtime enforcement still needs target-client or external-installer proof.", + "blocked_checks": [ + "Native enforcement" + ] + }, + { + "key": "native-client-telemetry", + "label_zh": "原生遥测", + "label_en": "Native Telemetry", + "category": "external", + "category_zh": "外部证据", + "category_en": "External evidence", + "status": "pending", + "summary_zh": "真实外部客户端 metadata-only 事件仍未导入。", + "summary_en": "Real external-client metadata-only events have not been imported yet.", + "blocked_checks": [ + "External events" + ] + } + ] + }, + "package_assets": { + "entries": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "file_count": 393, + "folder_count": 4, + "distribution": [ + { + "label": "SKILL.md", + "value": 1 + }, + { + "label": "README.md", + "value": 1 + }, + { + "label": "agents/interface.yaml", + "value": 1 + }, + { + "label": "manifest.json", + "value": 1 + }, + { + "label": "references", + "value": 35 + }, + { + "label": "scripts", + "value": 155 + }, + { + "label": "evals", + "value": 29 + }, + { + "label": "reports", + "value": 170 + } + ] + }, + "iteration_roadmap": { + "items": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ] + }, + "report_contract": { + "schema_version": "2.0", + "html_report": "reports/skill-overview.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": [ + "zh-CN", + "en" + ], + "layout": "kami-white-audit-v2", + "nav_labels": [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线" + ], + "nav_labels_en": [ + "Overview", + "Metrics", + "Profile", + "Principle", + "Contract", + "Quality", + "Risk", + "Assets", + "Roadmap" + ] + }, + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "display_name": "Yao Meta Skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "logic_steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "usage_steps": [ + "Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.", + "Use this skill when the request matches: Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + ], + "package_map": [ + { + "path": "SKILL.md", + "label": "Skill entrypoint", + "kind": "file", + "file_count": 1 + }, + { + "path": "README.md", + "label": "Human-readable usage guide", + "kind": "file", + "file_count": 1 + }, + { + "path": "agents/interface.yaml", + "label": "Neutral interface metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "manifest.json", + "label": "Lifecycle and portability metadata", + "kind": "file", + "file_count": 1 + }, + { + "path": "references", + "label": "Extended guidance and reusable notes", + "kind": "folder", + "file_count": 35 + }, + { + "path": "scripts", + "label": "Deterministic helpers or local tooling", + "kind": "folder", + "file_count": 155 + }, + { + "path": "evals", + "label": "Trigger and quality checks", + "kind": "folder", + "file_count": 29 + }, + { + "path": "reports", + "label": "Generated evidence and overview artifacts", + "kind": "folder", + "file_count": 170 + } + ], + "strengths": [ + "触发面保持精简,并锚定在 frontmatter description。", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。" + ], + "scenarios": [ + "把重复流程沉淀为可复用的 agent skill。", + "把分散提示词、对话记录或操作规范整理为稳定能力。", + "团队复用前,需要明确触发边界、质量证据和维护责任。", + "用户说出类似需求时:Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals." + ], + "trigger_contract": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "activation": "manual", + "execution": "inline", + "shell": "bash" + }, + "io_contract": { + "inputs": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "outputs": [ + "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "结构化 Skill 目录,共 8 类关键资产。" + ] + }, + "principles": [ + { + "title": "意图澄清", + "body": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse." + }, + { + "title": "边界路由", + "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。" + }, + { + "title": "资产分层", + "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。" + }, + { + "title": "证据回路", + "body": "Ask only the highest-leverage clarification before adding package weight." + }, + { + "title": "漂移观察", + "body": "Add near-neighbor exclusions and route evals before expanding workflow steps." + }, + { + "title": "杠杆升级", + "body": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + } + ], + "roadmap": [ + { + "title": "补齐世界证据", + "why": "世界级证据仍有 4 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": [ + "补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。", + "补齐人工盲评证据:盲评 pair 仍待真实 reviewer 决策。", + "按 ledger 总量继续补齐 4 项待补证据(外部 3 项、人工 1 项;未展开 2 项),并保持 claim guard 为 pending 状态。" + ], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger" + }, + { + "title": "Borrow one proven pattern on purpose", + "why": "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + "actions": [ + "Read the strongest pattern from obra/superpowers.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light." + ], + "unlocks": "A cleaner package shape with less accidental over-design." + }, + { + "title": "Harden portability semantics", + "why": "The skill already signals reuse across environments, so contract clarity matters early.", + "actions": [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations." + ], + "unlocks": "Safer cross-environment reuse with less target drift." + } + ], + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。" + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": "2.0.0", + "source_path": "skill-ir/examples/yao-meta-skill.json", + "target_count": 5, + "trigger_samples": 8, + "output_eval_cases": 3 + }, + "compiled_targets": { + "ok": true, + "schema_version": "1.0", + "summary": { + "target_count": 5, + "pass_count": 5, + "warn_count": 0, + "block_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "targets": [ + { + "target": "openai", + "status": "pass", + "adapter_mode": "metadata-adapter", + "degradation_strategy": "metadata-adapter", + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "permission_enforcement": "metadata-only", + "generated_files": [ + "targets/openai/adapter.json", + "targets/openai/agents/openai.yaml" + ], + "unsupported_features": [ + "client-native script permission prompts are represented as permission contract metadata" + ], + "warnings": [] + }, + { + "target": "claude", + "status": "pass", + "adapter_mode": "neutral-source-plus-adapter", + "degradation_strategy": "neutral-source-plus-adapter", + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "permission_enforcement": "metadata-fallback", + "generated_files": [ + "targets/claude/adapter.json", + "targets/claude/README.md" + ], + "unsupported_features": [ + "vendor-native metadata fields are carried as adapter JSON and README notes" + ], + "warnings": [] + }, + { + "target": "generic", + "status": "pass", + "adapter_mode": "agent-skills-compatible", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills compatible neutral package", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "targets/generic/adapter.json" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "agent-skills-compatible", + "status": "pass", + "adapter_mode": "neutral-agent-skills-source", + "degradation_strategy": "neutral-source", + "native_surface": "Agent Skills standard source tree", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "generated_files": [ + "SKILL.md", + "agents/interface.yaml" + ], + "unsupported_features": [], + "warnings": [] + }, + { + "target": "vscode", + "status": "pass", + "adapter_mode": "vscode-agent-skills-adapter", + "degradation_strategy": "agent-skills-source-with-vscode-notes", + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "permission_enforcement": "client-or-workspace-trust", + "generated_files": [ + "targets/vscode/adapter.json", + "targets/vscode/README.md" + ], + "unsupported_features": [ + "VS Code installation scope is documented but not installed by this compiler" + ], + "warnings": [] + } + ], + "failures": [], + "warnings": [] + }, + "output_quality": { + "case_count": 5, + "file_backed_case_count": 1, + "near_neighbor_case_count": 1, + "boundary_case_count": 1, + "baseline_pass_rate": 0.0, + "with_skill_pass_rate": 100.0, + "delta": 100.0, + "regression_count": 0, + "gate_pass": true, + "failure_taxonomy": [], + "blind_pair_count": 5 + }, + "output_execution": { + "ok": true, + "summary": { + "case_count": 5, + "variant_run_count": 10, + "command_executed_count": 10, + "model_executed_count": 10, + "recorded_fixture_count": 0, + "timing_observed_count": 10, + "token_observed_count": 10, + "token_estimated_count": 0, + "baseline_pass_rate": 23.67, + "with_skill_pass_rate": 43.67, + "delta": 20.0, + "regression_count": 0, + "failure_count": 0, + "gate_pass": true + }, + "runner": { + "mode": "command", + "command": [ + "python3", + "scripts/provider_output_eval_runner.py", + "--provider", + "deepseek", + "--model", + "deepseek-v4-flash", + "--api-format", + "chat-completions", + "--thinking", + "disabled", + "--api-key-env", + "DEEPSEEK_API_KEY" + ], + "timeout_seconds": 180.0 + }, + "failures": [] + }, + "output_blind_review": { + "summary": { + "pair_count": 5, + "answer_key_separate": true, + "with_skill_hidden_count": 5 + }, + "seed": "yao-output-eval-blind-v1", + "pair_count": 5, + "answer_key_separate": true + }, + "output_review_kit": { + "ok": true, + "summary": { + "case_count": 5, + "ready_for_adjudication_count": 0, + "pending_decision_count": 5, + "invalid_decision_count": 0, + "reviewer_metadata_present": false, + "answer_key_hidden": true, + "answer_key_path_exposed": false, + "ready_to_run_adjudication": false, + "failure_count": 0 + }, + "artifacts": { + "reviewer_kit_json": "reports/output_review_kit.json", + "reviewer_kit_markdown": "reports/output_review_kit.md", + "reviewer_kit_html": "reports/output_review_kit.html", + "blind_pack_json": "reports/output_blind_review_pack.json", + "blind_pack_markdown": "reports/output_blind_review_pack.md", + "decisions": "reports/output_review_decisions.json", + "adjudication_json": "reports/output_review_adjudication.json", + "adjudication_markdown": "reports/output_review_adjudication.md" + }, + "failures": [] + }, + "output_review_adjudication": { + "ok": true, + "summary": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "agreement_count": 0, + "disagreement_count": 0, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "pending_answer_hidden_count": 5, + "agreement_rate": null, + "needs_review": true, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "blind_review_completed_before_answer_key": false, + "answer_key_not_opened_before_decisions": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "failure_count": 0, + "reviewer_checklist_count": 5, + "reviewer_checklist_pending_count": 5, + "reviewer_checklist_invalid_count": 0, + "reviewer_checklist_ready_count": 0 + }, + "reviewer": "", + "reviewed_at": "", + "failures": [] + }, + "benchmark_reproducibility": { + "ok": true, + "summary": { + "reproducibility_ready": true, + "release_lock_ready": false, + "methodology_complete": true, + "required_artifact_count": 25, + "missing_artifact_count": 0, + "evidence_bundle_sha256": "6c95043c2d8513102b37bc70a01647246933c84e8a27c2748cc2cae20e040158", + "source_contract_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "output_case_count": 5, + "failure_disclosure_count": 3, + "command_count": 23, + "command_executed_count": 10, + "timing_observed_count": 10, + "model_executed_count": 10, + "token_observed_count": 10, + "human_review_complete": false, + "provider_evidence_complete": true, + "world_class_ready": false, + "world_class_open_gap_count": 3, + "world_class_task_count": 4, + "world_class_ledger_pending_count": 4, + "world_class_source_check_count": 19, + "world_class_source_pass_count": 12, + "world_class_source_blocked_count": 7, + "beta_test_ready": false, + "beta_test_blocker_count": 1, + "beta_test_deferred_evidence_count": 4, + "public_claim_ready": false, + "public_claim_blocker_count": 4, + "working_tree_dirty": true, + "changed_file_count": 37, + "source_tree_dirty": true, + "source_changed_file_count": 1, + "generated_tree_dirty": true, + "generated_changed_file_count": 36 + }, + "commit": "24bea607a2b16e766bc8f2bc8483f22fae1edc61", + "missing_artifacts": [], + "limitations": [ + "The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately.", + "Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet.", + "Pending blind-review decisions are visible but do not count as human adjudication.", + "World-class readiness remains false until external and human evidence gaps close.", + "Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims." + ] + }, + "runtime_conformance": { + "target_count": 5, + "pass_count": 5, + "fail_count": 0 + }, + "runtime_permissions": { + "ok": true, + "summary": { + "target_count": 4, + "pass_count": 4, + "fail_count": 0, + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "residual_risk_count": 4, + "required_capability_count": 3, + "failure_count": 0, + "installer_enforcement_source_status": "present", + "installer_enforcement_target_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "installer_permission_capability_count": 3, + "world_class_native_evidence_ready": false, + "installer_enforcement_ready": true + }, + "expected_capabilities": [ + "file_write", + "network", + "subprocess" + ], + "targets": [ + { + "target": "openai", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "claude", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "generic", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + }, + { + "target": "vscode", + "status": "pass", + "assurance": "metadata-fallback-explicit", + "native_enforcement": false, + "metadata_fallback_explicit": true, + "residual_risks": [ + "Client-native permission enforcement is not provided by this target; installer or operator must honor metadata." + ] + } + ], + "failures": [] + }, + "trust_security": { + "scanned_files": 247, + "script_count": 155, + "internal_module_count": 69, + "secret_findings": 0, + "dependency_files": [ + "requirements-ci.txt" + ], + "network_script_count": 3, + "network_policy_covered_count": 3, + "network_policy_missing_count": 0, + "file_write_script_count": 74, + "permission_required_count": 3, + "permission_approved_count": 3, + "permission_missing_count": 0, + "permission_invalid_count": 0, + "permission_expired_count": 0, + "help_smoke_checked_count": 86, + "help_smoke_failed_count": 0, + "interactive_script_count": 0, + "package_hash_scope": "source-contract-without-generated-reports", + "package_hash_file_count": 247, + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + }, + "skill_atlas": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + }, + "registry_distribution": { + "ok": true, + "package": { + "schema_version": "2.0", + "name": "yao-meta-skill", + "version": "1.1.0", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "maturity": "governed", + "owner": "Yao Team", + "review_cadence": "quarterly", + "trust_level": "local", + "license": "MIT", + "checksums": { + "package_sha256": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c", + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + "compatibility": { + "openai": "pass", + "claude": "pass", + "agent-skills": "pass", + "vscode": "pass", + "generic": "pass", + "agent-skills-compatible": "pass" + }, + "source": { + "skill_dir": ".", + "skill_ir": "skill-ir/examples/yao-meta-skill.json", + "ir_schema_version": "2.0.0", + "canonical_metadata": "agents/interface.yaml" + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "package_metadata": "registry/packages/yao-meta-skill.json" + }, + "distribution": { + "archive_verified": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "package_verification": "reports/package_verification.json", + "install_simulated": true, + "install_simulation": "reports/install_simulation.json" + }, + "generated_at": "2026-06-13" + }, + "failures": [], + "warnings": [] + }, + "package_verification": { + "ok": true, + "summary": { + "target_count": 4, + "adapter_count": 4, + "archive_present": true, + "archive_sha256": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7", + "archive_entry_count": 580, + "nested_skill_entry_count": 0, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "install_simulation": { + "ok": true, + "summary": { + "archive_present": true, + "archive_entry_count": 598, + "nested_skill_entry_count": 0, + "archive_extracted": true, + "entrypoint_loaded": true, + "manifest_loaded": true, + "interface_loaded": true, + "adapter_count": 4, + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 4, + "permission_capability_count": 3, + "install_root_is_temp": false, + "failure_count": 0, + "warning_count": 0 + }, + "failures": [], + "warnings": [] + }, + "upgrade_check": { + "ok": true, + "summary": { + "declared_bump": "minor", + "recommended_bump": "minor", + "breaking_change_count": 0, + "added_target_count": 2, + "metadata_change_count": 1, + "checksum_change_count": 2, + "failure_count": 0, + "warning_count": 0 + }, + "upgrade_diff": { + "name_changed": false, + "added_targets": [ + "agent-skills-compatible", + "vscode" + ], + "removed_targets": [], + "compatibility_changes": [ + { + "target": "agent-skills", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "agent-skills-compatible", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "vscode", + "from": "missing", + "to": "pass", + "direction": "improved" + } + ], + "metadata_changes": [ + { + "field": "description", + "from": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes.", + "to": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + } + ], + "checksum_changes": [ + { + "field": "archive_sha256", + "from": "", + "to": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + { + "field": "package_sha256", + "from": "0000000000000000000000000000000000000000000000000000000000000000", + "to": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "release_notes": [ + "Recommended version bump: minor.", + "Added targets: agent-skills-compatible, vscode.", + "Package or archive checksum changed; reviewers should verify package artifacts before release.", + "Package verification evidence: reports/package_verification.md." + ], + "failures": [], + "warnings": [] + }, + "adoption_drift": { + "ok": true, + "summary": { + "event_count": 1, + "adoption_sample_count": 1, + "activation_count": 1, + "accepted_count": 1, + "edited_count": 0, + "rejected_count": 0, + "missed_count": 0, + "failed_count": 0, + "adoption_rate": 100.0, + "missed_trigger_count": 0, + "wrong_trigger_count": 0, + "bad_output_count": 0, + "script_error_count": 0, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": "low", + "event_types": { + "skill_activation": 1 + }, + "failure_types": {}, + "source_types": { + "manual": 1 + }, + "command_counts": {} + }, + "next_iteration_candidates": [], + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": false, + "raw_event_log_packaged": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ] + }, + "failures": [] + }, + "review_waivers": { + "ok": true, + "summary": { + "waiver_count": 0, + "active_count": 0, + "expired_count": 0, + "invalid_count": 0, + "covered_gate_count": 0, + "covered_gate_keys": [], + "waiver_candidate_count": 2, + "waiverable_open_count": 1, + "non_waivable_count": 1 + }, + "policy": { + "blocker_waivers_allowed": false, + "minimum_reason_chars": 20, + "expires_required": true, + "review_studio_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "review-waivers", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "world-class-evidence" + ], + "known_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "waiverable_gate_keys": [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "permission-gates", + "permission-runtime", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report" + ], + "non_waivable_gate_keys": [ + "review-waivers", + "world-class-evidence" + ] + }, + "failures": [], + "warnings": [] + }, + "review_annotations": { + "ok": true, + "summary": { + "annotation_count": 0, + "open_count": 0, + "resolved_count": 0, + "deferred_count": 0, + "open_blocker_count": 0, + "open_warning_count": 0, + "linked_gate_count": 0, + "target_missing_count": 0, + "failure_count": 0 + }, + "annotations": [], + "failures": [] + }, + "world_class_evidence_plan": { + "ok": true, + "summary": { + "audit_decision": "continue-iteration", + "world_class_ready": false, + "audit_world_class_ready": false, + "ready_to_claim_world_class": false, + "ledger_completion_required": true, + "evidence_requirement_count": 4, + "task_count": 4, + "human_task_count": 1, + "external_task_count": 3, + "review_task_count": 0, + "decision": "collect-external-evidence" + }, + "tasks": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "status": "pass", + "category": "external", + "owner": "operator with provider credentials", + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "audit_next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "category": "human", + "owner": "human reviewer", + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "audit_next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "category": "external", + "owner": "target client or installer integrator", + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "audit_next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "audit_next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_audit": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "open_gap_count": 3 + } + }, + "world_class_evidence_ledger": { + "ok": true, + "summary": { + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "entries": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "pending", + "source_status": "pass", + "source_accepted": true, + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "pending", + "source_status": "human_required", + "source_accepted": false, + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_plan": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "task_count": 4, + "evidence_requirement_count": 4 + } + }, + "synthesis_highlights": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "artifact_design": { + "design_system": "metric editorial", + "highlights": [ + "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities." + ] + }, + "prompt_quality": { + "overall_quality_score": 89.0, + "highlights": [ + "Primary prompt task family: Execution operation.", + "Complexity: expert — multiple task families plus governance, evaluation, or expert-level constraints", + "Completeness: 100/100.", + "Clarity: 85/100." + ] + }, + "system_model": { + "stability": { + "score": 100, + "band": "system-ready" + }, + "highlights": [ + "Stability: system-ready (100/100).", + "Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "Leverage: Tune the frontmatter description — Name the recurring job, expected input, output, and strongest non-goal in compact language.", + "Leverage: Install output self-repair checks — Add only the checks that prevent recurring output mistakes." + ] + }, + "metadata": { + "canonical_format": "agent-skills", + "targets": [ + "openai", + "claude", + "generic", + "vscode" + ], + "maturity_tier": "governed", + "skill_archetype": "governed", + "updated_at": "2026-03-31" + } +} \ No newline at end of file diff --git a/reports/skill_atlas.html b/reports/skill_atlas.html new file mode 100644 index 0000000..ec5ab05 --- /dev/null +++ b/reports/skill_atlas.html @@ -0,0 +1,57 @@ + + + + + + Skill Atlas + + + +
    +

    Skill Atlas

    +

    Portfolio-level review for route overlap, stale ownership, shared resources, telemetry drift, and no-route opportunities.

    +
    +
    Skills11
    +
    Actionable1
    +
    Route Collisions0
    +
    Owner Gaps0
    +
    Stale Skills0
    +
    Drift Signals0
    +
    No-Route Opportunities0
    +
    +
    +

    Actionable Issues

    +
    • No blocking portfolio issues detected.
    +
    +
    +

    No-Route Opportunities

    +

    Missed-trigger telemetry and explicit failure cases become candidate routing work without storing raw prompts or outputs.

    +
    • No no-route opportunities detected.
    +
    +
    +

    Full Portfolio Counts

    +

    All scanned skills remain visible: 5 total route collisions, 8 total owner gaps, 9 total stale signals, and 0 telemetry drift signals.

    +
    +
    +

    Catalog

    + + + +
    NamePathOwnerMaturityReviewScope
    yao-meta-skill.Yao Teamgovernedquarterlyrelease
    release-orchestratorexamples/complex-release-orchestrator/generated-skillYao Teamproductionquarterlyexample
    frontend-reviewexamples/evolution-frontend-review/finalmissingunknownmissingexample
    frontend-reviewexamples/evolution-frontend-review/v0missingunknownmissingexample
    frontend-reviewexamples/evolution-frontend-review/v1missingunknownmissingexample
    incident-command-governorexamples/governed-incident-command/generated-skillIncident Operations Teamgovernedmonthlyexample
    note-cleanupexamples/simple-note-cleanup/generated-skillmissingunknownmissingexample
    frontend-reviewexamples/team-frontend-review/generated-skillmissingunknownmissingexample
    invalid-governance-skilltests/fixtures/governance_invalid_manifestmissinggovernedmissingtest-fixture
    broken-yaml-skilltests/fixtures/package_invalid_yamlmissingunknownmissingtest-fixture
    broken-skilltests/fixtures/package_missing_interface_fieldmissingunknownmissingtest-fixture
    +
    +
    + + diff --git a/reports/skill_atlas.json b/reports/skill_atlas.json new file mode 100644 index 0000000..8644937 --- /dev/null +++ b/reports/skill_atlas.json @@ -0,0 +1,1033 @@ +{ + "ok": true, + "workspace_root": ".", + "summary": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + }, + "scope_policy": { + "present": true, + "path": "skill_atlas/policy.json", + "schema_version": "1.0", + "rules": [ + { + "path_prefix": "examples/", + "scope": "example", + "actionable": false, + "reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package." + }, + { + "path_prefix": "tests/fixtures/", + "scope": "test-fixture", + "actionable": false, + "reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers." + }, + { + "path_prefix": "geo-ranking-article-generator", + "scope": "embedded-generated-skill", + "actionable": false, + "reason": "This nested generated skill is retained as local evidence and should not gate the root Meta Skill release." + } + ] + }, + "catalog": { + "workspace_root": ".", + "generated_at": "2026-06-13", + "skills": [ + { + "name": "yao-meta-skill", + "path": ".", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "owner": "Yao Team", + "version": "1.1.0", + "status": "active", + "maturity": "governed", + "updated_at": "2026-03-31", + "review_cadence": "quarterly", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "resources": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py", + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md", + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "token_set": [ + "checks", + "docs", + "evals", + "evaluate", + "exclude", + "improve", + "install", + "migration", + "no-skill", + "package", + "prompts", + "release", + "routing", + "scripts", + "sops", + "summary", + "tests", + "translation", + "trigger", + "workflows", + "优化已有", + "评测" + ], + "atlas_scope": "release", + "actionable": true, + "scope_reason": "default release-actionable skill", + "telemetry": { + "report_present": true, + "report": "reports/adoption_drift_report.json", + "risk_band": "low", + "event_count": 1, + "adoption_sample_count": 1, + "adoption_rate": 100.0, + "candidate_count": 0 + } + }, + { + "name": "release-orchestrator", + "path": "examples/complex-release-orchestrator/generated-skill", + "description": "Coordinate software release preparation, rollout readiness, migration notes, rollback planning, stakeholder communication, and final go/no-go release packets. Use when asked to prepare a release packet, review release readiness, or turn scattered launch notes into one reusable release workflow. Do not use for generic project planning, one-off announcement drafting, or high-level release theory.", + "owner": "Yao Team", + "version": "1.0.0", + "status": "active", + "maturity": "production", + "updated_at": "2026-03-31", + "review_cadence": "quarterly", + "targets": [], + "resources": [ + "scripts/build_release_packet.py", + "references/migration-template.md", + "references/release-checklist.md", + "references/risk-matrix.md", + "references/stakeholder-comms.md" + ], + "token_set": [ + "announcement", + "asked", + "communication", + "coordinate", + "do", + "drafting", + "final", + "generic", + "go", + "high-level", + "launch", + "migration", + "no-go", + "not", + "notes", + "one", + "one-off", + "or", + "packet", + "packets", + "planning", + "preparation", + "prepare", + "project", + "readiness", + "release", + "review", + "rollback", + "rollout", + "scattered", + "software", + "stakeholder", + "theory", + "workflow" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/complex-release-orchestrator/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "a11y", + "accessibility", + "asked", + "code", + "common", + "components", + "forms", + "frontend", + "issues", + "missing", + "or", + "pre-merge", + "quality", + "react", + "regressions", + "review", + "risky", + "security", + "states", + "ui", + "ux" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/final/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "description": "Review frontend code before merge.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "before", + "code", + "frontend", + "merge", + "review" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/v0/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "description": "Review frontend code for accessibility, risky UI security issues, missing loading and error states, and pre-merge regressions. Use when asked to review React, UI, forms, frontend components, or merge-readiness for the frontend.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "accessibility", + "asked", + "code", + "components", + "error", + "forms", + "frontend", + "issues", + "loading", + "merge-readiness", + "missing", + "or", + "pre-merge", + "react", + "regressions", + "review", + "risky", + "security", + "states", + "ui" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/v1/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "owner": "Incident Operations Team", + "version": "1.0.0", + "status": "active", + "maturity": "governed", + "updated_at": "2026-03-31", + "review_cadence": "monthly", + "targets": [ + "openai", + "claude", + "generic" + ], + "resources": [ + "scripts/build_incident_packet.py", + "references/comms-policy.md", + "references/review-policy.md", + "references/severity-matrix.md" + ], + "token_set": [ + "asked", + "assemble", + "assessment", + "build", + "command", + "communication", + "governed", + "incident", + "or", + "packets", + "review", + "run", + "severity", + "standardize" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/governed-incident-command/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "description": "Clean messy meeting notes into structured markdown summaries. Use when asked to organize meeting notes, extract action items, separate decisions from open questions, or turn rough notes into a clean recap.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "action", + "asked", + "clean", + "decisions", + "extract", + "items", + "markdown", + "meeting", + "messy", + "notes", + "open", + "or", + "organize", + "questions", + "recap", + "rough", + "separate", + "structured", + "summaries" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/simple-note-cleanup/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [ + "references/checklist.md" + ], + "token_set": [ + "a11y", + "accessibility", + "asked", + "check", + "code", + "components", + "frontend", + "missing", + "or", + "pre-merge", + "react", + "regressions", + "rendering", + "review", + "run", + "security", + "states", + "ui", + "unsafe", + "ux" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/team-frontend-review/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "description": "Test fixture for governance validation.", + "owner": "", + "version": "1.0.0", + "status": "frozen", + "maturity": "governed", + "updated_at": "2026/03/31", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "fixture", + "governance", + "test", + "validation" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/governance_invalid_manifest/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "description": "Broken YAML fixture for packager failure testing.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "broken", + "failure", + "fixture", + "packager", + "testing", + "yaml" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/package_invalid_yaml/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "description": "Broken fixture for packager failure testing.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "broken", + "failure", + "fixture", + "packager", + "testing" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/package_missing_interface_field/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + } + ], + "summary": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + } + }, + "route_collisions": [ + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/final", + "path_b": "examples/evolution-frontend-review/v1", + "score": 0.708, + "status": "collision", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + }, + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/final", + "path_b": "examples/team-frontend-review/generated-skill", + "score": 0.64, + "status": "collision", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + }, + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/v1", + "path_b": "examples/team-frontend-review/generated-skill", + "score": 0.538, + "status": "collision", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + }, + { + "skill_a": "broken-yaml-skill", + "skill_b": "broken-skill", + "path_a": "tests/fixtures/package_invalid_yaml", + "path_b": "tests/fixtures/package_missing_interface_field", + "score": 0.833, + "status": "collision", + "actionable": false, + "scope_a": "test-fixture", + "scope_b": "test-fixture" + }, + { + "skill_a": "frontend-review", + "skill_b": "frontend-review", + "path_a": "examples/evolution-frontend-review/final", + "path_b": "examples/evolution-frontend-review/v0", + "score": 1.0, + "status": "duplicate-name", + "actionable": false, + "scope_a": "example", + "scope_b": "example" + } + ], + "actionable_route_collisions": [], + "dependency_graph": { + "nodes": [ + { + "name": "yao-meta-skill", + "path": "." + }, + { + "name": "release-orchestrator", + "path": "examples/complex-release-orchestrator/generated-skill" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1" + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field" + } + ], + "shared_resources": [] + }, + "stale_skills": [ + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill", + "reason": "review overdue by cadence monthly", + "age_days": 74, + "allowed_days": 31, + "actionable": false, + "scope": "example" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + } + ], + "actionable_stale_skills": [], + "owner_review_gaps": [ + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "missing": [ + "owner", + "review_cadence" + ], + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "test-fixture" + } + ], + "actionable_owner_review_gaps": [], + "drift_signals": [], + "actionable_drift_signals": [], + "no_route_opportunities": [], + "artifacts": { + "catalog": "skill_atlas/catalog.json", + "route_overlap_matrix": "skill_atlas/route_overlap_matrix.csv", + "dependency_graph": "skill_atlas/dependency_graph.json", + "stale_skills": "skill_atlas/stale_skills.json", + "owner_review_gaps": "skill_atlas/owner_review_gaps.json", + "drift_signals": "skill_atlas/drift_signals.json", + "no_route_opportunities": "skill_atlas/no_route_opportunities.json", + "report_json": "reports/skill_atlas.json", + "report_html": "reports/skill_atlas.html" + } +} diff --git a/reports/skill_os2_audit.json b/reports/skill_os2_audit.json new file mode 100644 index 0000000..f320b19 --- /dev/null +++ b/reports/skill_os2_audit.json @@ -0,0 +1,438 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-13", + "skill_dir": ".", + "summary": { + "item_count": 15, + "pass_count": 12, + "warn_count": 0, + "human_required_count": 1, + "external_required_count": 2, + "missing_count": 0, + "open_gap_count": 3, + "local_foundation_ready": true, + "world_class_ready": false, + "decision": "continue-iteration" + }, + "status_counts": { + "pass": 12, + "warn": 0, + "human_required": 1, + "external_required": 2, + "missing": 0 + }, + "items": [ + { + "key": "skill-ir", + "label": "Skill IR", + "status": "pass", + "current": "schema 2.0.0; targets 5", + "target": "2.0 schema, root export, and target-neutral contract evidence", + "evidence": [ + { + "path": "skill-ir/schema.json", + "exists": true + }, + { + "path": "skill-ir/examples/yao-meta-skill.json", + "exists": true + }, + { + "path": "references/skill-ir-method.md", + "exists": true + } + ], + "next_action": "Keep IR as the source before target packaging." + }, + { + "key": "target-compiler", + "label": "Target Compiler", + "status": "pass", + "current": "5/5 targets pass", + "target": "OpenAI, Claude, generic, Agent Skills compatible, and VS Code contracts generated from IR", + "evidence": [ + { + "path": "scripts/compile_skill.py", + "exists": true + }, + { + "path": "reports/compiled_targets.json", + "exists": true + }, + { + "path": "tests/verify_compile_skill.py", + "exists": true + } + ], + "next_action": "Deepen target-native transforms when provider clients expose stronger runtime APIs." + }, + { + "key": "output-eval-lab", + "label": "Output Eval Lab", + "status": "pass", + "current": "5 cases; delta 100.0; exec 10; blind 5", + "target": "with-skill/baseline, assertions, execution evidence, blind A/B, failure taxonomy", + "evidence": [ + { + "path": "evals/output/cases.jsonl", + "exists": true + }, + { + "path": "scripts/run_output_eval.py", + "exists": true + }, + { + "path": "scripts/run_output_execution.py", + "exists": true + }, + { + "path": "reports/output_quality_scorecard.json", + "exists": true + }, + { + "path": "reports/output_execution_runs.json", + "exists": true + }, + { + "path": "reports/output_blind_review_pack.json", + "exists": true + } + ], + "next_action": "Add more real-file and adversarial holdout cases as usage grows." + }, + { + "key": "provider-holdout", + "label": "Provider Holdout", + "status": "pass", + "current": "model-executed 10; token-observed 10", + "target": "At least one real provider-backed holdout run with observed model/timing/token metadata", + "evidence": [ + { + "path": "scripts/provider_output_eval_runner.py", + "exists": true + }, + { + "path": "reports/output_execution_runs.json", + "exists": true + } + ], + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "current": "0/5 decisions; pending 5", + "target": "Real reviewer decisions, blind-review attestation, and integrity fingerprints recorded before claiming output review completion", + "evidence": [ + { + "path": "reports/output_review_decisions.json", + "exists": true + }, + { + "path": "reports/output_review_adjudication.json", + "exists": true + }, + { + "path": "scripts/adjudicate_output_review.py", + "exists": true + } + ], + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "benchmark-reproducibility", + "label": "Benchmark Reproducibility", + "status": "pass", + "current": "artifacts 25; missing 0; failures 3", + "target": "Public methodology, reproducible commands, required artifacts, and failure disclosure are machine-checkable", + "evidence": [ + { + "path": "reports/benchmark_methodology.md", + "exists": true + }, + { + "path": "reports/benchmark_reproducibility.json", + "exists": true + }, + { + "path": "reports/benchmark_reproducibility.md", + "exists": true + }, + { + "path": "evals/failure-cases.md", + "exists": true + }, + { + "path": "tests/verify_benchmark_reproducibility.py", + "exists": true + } + ], + "next_action": "Keep the manifest current with every benchmark, package, and release evidence change." + }, + { + "key": "runtime-conformance", + "label": "Runtime Conformance", + "status": "pass", + "current": "5/5 targets pass", + "target": "Target package structure, metadata, relative paths, and degradation notes pass", + "evidence": [ + { + "path": "runtime/conformance/schema.json", + "exists": true + }, + { + "path": "scripts/run_conformance_suite.py", + "exists": true + }, + { + "path": "reports/conformance_matrix.json", + "exists": true + } + ], + "next_action": "Keep target conformance fixtures updated as platform contracts change." + }, + { + "key": "trust-security", + "label": "Trust Security", + "status": "pass", + "current": "secrets 0; scripts 155; help failures 0", + "target": "Secrets, scripts, dependencies, permissions, and package hash are reviewable", + "evidence": [ + { + "path": "scripts/trust_check.py", + "exists": true + }, + { + "path": "reports/security_trust_report.json", + "exists": true + }, + { + "path": "security/permission_policy.json", + "exists": true + } + ], + "next_action": "Keep high-permission approvals scoped, expiring, and target-mapped." + }, + { + "key": "runtime-permission-metadata", + "label": "Permission Metadata", + "status": "pass", + "current": "4/4 target probes pass; metadata fallback 4; installer enforcement 4", + "target": "Packaged adapters expose explicit permission metadata, residual risks, and installer enforcement evidence when available", + "evidence": [ + { + "path": "scripts/probe_runtime_permissions.py", + "exists": true + }, + { + "path": "reports/runtime_permission_probes.json", + "exists": true + } + ], + "next_action": "Preserve residual-risk notes until real native enforcement exists." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "current": "native-enforced targets 0; installer-enforced targets 4", + "target": "At least one target/client enforces approved permissions at runtime", + "evidence": [ + { + "path": "reports/runtime_permission_probes.json", + "exists": true + }, + { + "path": "reports/install_simulation.json", + "exists": true + }, + { + "path": "security/permission_policy.json", + "exists": true + } + ], + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "skill-atlas", + "label": "Skill Atlas", + "status": "pass", + "current": "11 skills; actionable collisions 0", + "target": "Workspace catalog, route overlap, stale/owner gaps, drift, and no-route opportunities", + "evidence": [ + { + "path": "scripts/build_skill_atlas.py", + "exists": true + }, + { + "path": "skill_atlas/catalog.json", + "exists": true + }, + { + "path": "reports/skill_atlas.json", + "exists": true + }, + { + "path": "skill_atlas/policy.json", + "exists": true + } + ], + "next_action": "Feed real drift data into Atlas once client telemetry is installed." + }, + { + "key": "registry-distribution", + "label": "Registry Distribution", + "status": "pass", + "current": "zip entries 580; install failures 0; permission failures 0", + "target": "Package metadata, archive checksum, package verification, and install simulation pass", + "evidence": [ + { + "path": "registry/packages/yao-meta-skill.json", + "exists": true + }, + { + "path": "reports/package_verification.json", + "exists": true + }, + { + "path": "reports/install_simulation.json", + "exists": true + } + ], + "next_action": "Regenerate registry after package verification so checksums stay aligned." + }, + { + "key": "review-studio", + "label": "Review Studio", + "status": "pass", + "current": "decision review; warnings 3; score 91", + "target": "One page shows gates, evidence paths, blockers, warnings, actions, waivers, and annotations", + "evidence": [ + { + "path": "scripts/render_review_studio.py", + "exists": true + }, + { + "path": "reports/review-studio.json", + "exists": true + }, + { + "path": "reports/review-studio.html", + "exists": true + } + ], + "next_action": "Resolve human/external warning gates before claiming full release readiness." + }, + { + "key": "telemetry-drift", + "label": "Telemetry Drift", + "status": "pass", + "current": "events 1; risk low; recipes 5", + "target": "Local-first metadata-only event contract, aggregate drift report, hook recipes, and import path", + "evidence": [ + { + "path": "reports/adoption_drift_report.json", + "exists": true + }, + { + "path": "reports/telemetry_hook_recipes.json", + "exists": true + }, + { + "path": "scripts/import_telemetry_events.py", + "exists": true + } + ], + "next_action": "Keep raw JSONL out of distributed packages and use aggregate reports for Atlas." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "current": "external source events 0; adoption samples 1", + "target": "A real Browser/Chrome/provider client sends production metadata events", + "evidence": [ + { + "path": "scripts/telemetry_native_host.py", + "exists": true + }, + { + "path": "reports/adoption_drift_report.json", + "exists": true + } + ], + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "next_highest_leverage": [ + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "current": "0/5 decisions; pending 5", + "target": "Real reviewer decisions, blind-review attestation, and integrity fingerprints recorded before claiming output review completion", + "evidence": [ + { + "path": "reports/output_review_decisions.json", + "exists": true + }, + { + "path": "reports/output_review_adjudication.json", + "exists": true + }, + { + "path": "scripts/adjudicate_output_review.py", + "exists": true + } + ], + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "current": "native-enforced targets 0; installer-enforced targets 4", + "target": "At least one target/client enforces approved permissions at runtime", + "evidence": [ + { + "path": "reports/runtime_permission_probes.json", + "exists": true + }, + { + "path": "reports/install_simulation.json", + "exists": true + }, + { + "path": "security/permission_policy.json", + "exists": true + } + ], + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "current": "external source events 0; adoption samples 1", + "target": "A real Browser/Chrome/provider client sends production metadata events", + "evidence": [ + { + "path": "scripts/telemetry_native_host.py", + "exists": true + }, + { + "path": "reports/adoption_drift_report.json", + "exists": true + } + ], + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "artifacts": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "world_class_evidence_plan": "reports/world_class_evidence_plan.md" + } +} diff --git a/reports/skill_os2_audit.md b/reports/skill_os2_audit.md new file mode 100644 index 0000000..ca3c1a3 --- /dev/null +++ b/reports/skill_os2_audit.md @@ -0,0 +1,101 @@ +# Skill OS 2.0 Audit + +Generated at: `2026-06-13` + +## Summary + +- decision: `continue-iteration` +- pass: `12` / `15` +- human required: `1` +- external required: `2` +- missing: `0` +- world-class ready: `false` +- evidence plan: `reports/world_class_evidence_plan.md` + +## Audit Items + +| Area | Status | Current | Target | Next action | +| --- | --- | --- | --- | --- | +| Skill IR | pass | schema 2.0.0; targets 5 | 2.0 schema, root export, and target-neutral contract evidence | Keep IR as the source before target packaging. | +| Target Compiler | pass | 5/5 targets pass | OpenAI, Claude, generic, Agent Skills compatible, and VS Code contracts generated from IR | Deepen target-native transforms when provider clients expose stronger runtime APIs. | +| Output Eval Lab | pass | 5 cases; delta 100.0; exec 10; blind 5 | with-skill/baseline, assertions, execution evidence, blind A/B, failure taxonomy | Add more real-file and adversarial holdout cases as usage grows. | +| Provider Holdout | pass | model-executed 10; token-observed 10 | At least one real provider-backed holdout run with observed model/timing/token metadata | Run provider-backed holdout cases with real credentials and commit only aggregate evidence. | +| Human Adjudication | human-required | 0/5 decisions; pending 5 | Real reviewer decisions, blind-review attestation, and integrity fingerprints recorded before claiming output review completion | Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication. | +| Benchmark Reproducibility | pass | artifacts 25; missing 0; failures 3 | Public methodology, reproducible commands, required artifacts, and failure disclosure are machine-checkable | Keep the manifest current with every benchmark, package, and release evidence change. | +| Runtime Conformance | pass | 5/5 targets pass | Target package structure, metadata, relative paths, and degradation notes pass | Keep target conformance fixtures updated as platform contracts change. | +| Trust Security | pass | secrets 0; scripts 155; help failures 0 | Secrets, scripts, dependencies, permissions, and package hash are reviewable | Keep high-permission approvals scoped, expiring, and target-mapped. | +| Permission Metadata | pass | 4/4 target probes pass; metadata fallback 4; installer enforcement 4 | Packaged adapters expose explicit permission metadata, residual risks, and installer enforcement evidence when available | Preserve residual-risk notes until real native enforcement exists. | +| Native Permission Enforcement | external-required | native-enforced targets 0; installer-enforced targets 4 | At least one target/client enforces approved permissions at runtime | Integrate a real target-client or external installer runtime guard before claiming native permission enforcement. | +| Skill Atlas | pass | 11 skills; actionable collisions 0 | Workspace catalog, route overlap, stale/owner gaps, drift, and no-route opportunities | Feed real drift data into Atlas once client telemetry is installed. | +| Registry Distribution | pass | zip entries 580; install failures 0; permission failures 0 | Package metadata, archive checksum, package verification, and install simulation pass | Regenerate registry after package verification so checksums stay aligned. | +| Review Studio | pass | decision review; warnings 3; score 91 | One page shows gates, evidence paths, blockers, warnings, actions, waivers, and annotations | Resolve human/external warning gates before claiming full release readiness. | +| Telemetry Drift | pass | events 1; risk low; recipes 5 | Local-first metadata-only event contract, aggregate drift report, hook recipes, and import path | Keep raw JSONL out of distributed packages and use aggregate reports for Atlas. | +| Native Client Telemetry | external-required | external source events 0; adoption samples 1 | A real Browser/Chrome/provider client sends production metadata events | Install a real client against the native host and import production metadata-only events. | + +## Open Highest-Leverage Gaps + +- `human-adjudication` (human-required): Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication. +- `native-permission-enforcement` (external-required): Integrate a real target-client or external installer runtime guard before claiming native permission enforcement. +- `native-client-telemetry` (external-required): Install a real client against the native host and import production metadata-only events. + +## Evidence + +### Skill IR + +- existing evidence: `skill-ir/schema.json`, `skill-ir/examples/yao-meta-skill.json`, `references/skill-ir-method.md` + +### Target Compiler + +- existing evidence: `scripts/compile_skill.py`, `reports/compiled_targets.json`, `tests/verify_compile_skill.py` + +### Output Eval Lab + +- existing evidence: `evals/output/cases.jsonl`, `scripts/run_output_eval.py`, `scripts/run_output_execution.py`, `reports/output_quality_scorecard.json`, `reports/output_execution_runs.json`, `reports/output_blind_review_pack.json` + +### Provider Holdout + +- existing evidence: `scripts/provider_output_eval_runner.py`, `reports/output_execution_runs.json` + +### Human Adjudication + +- existing evidence: `reports/output_review_decisions.json`, `reports/output_review_adjudication.json`, `scripts/adjudicate_output_review.py` + +### Benchmark Reproducibility + +- existing evidence: `reports/benchmark_methodology.md`, `reports/benchmark_reproducibility.json`, `reports/benchmark_reproducibility.md`, `evals/failure-cases.md`, `tests/verify_benchmark_reproducibility.py` + +### Runtime Conformance + +- existing evidence: `runtime/conformance/schema.json`, `scripts/run_conformance_suite.py`, `reports/conformance_matrix.json` + +### Trust Security + +- existing evidence: `scripts/trust_check.py`, `reports/security_trust_report.json`, `security/permission_policy.json` + +### Permission Metadata + +- existing evidence: `scripts/probe_runtime_permissions.py`, `reports/runtime_permission_probes.json` + +### Native Permission Enforcement + +- existing evidence: `reports/runtime_permission_probes.json`, `reports/install_simulation.json`, `security/permission_policy.json` + +### Skill Atlas + +- existing evidence: `scripts/build_skill_atlas.py`, `skill_atlas/catalog.json`, `reports/skill_atlas.json`, `skill_atlas/policy.json` + +### Registry Distribution + +- existing evidence: `registry/packages/yao-meta-skill.json`, `reports/package_verification.json`, `reports/install_simulation.json` + +### Review Studio + +- existing evidence: `scripts/render_review_studio.py`, `reports/review-studio.json`, `reports/review-studio.html` + +### Telemetry Drift + +- existing evidence: `reports/adoption_drift_report.json`, `reports/telemetry_hook_recipes.json`, `scripts/import_telemetry_events.py` + +### Native Client Telemetry + +- existing evidence: `scripts/telemetry_native_host.py`, `reports/adoption_drift_report.json` diff --git a/reports/skill_os2_coverage.json b/reports/skill_os2_coverage.json new file mode 100644 index 0000000..3c8c43e --- /dev/null +++ b/reports/skill_os2_coverage.json @@ -0,0 +1,811 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "item_count": 21, + "module_count": 8, + "recommended_pr_count": 13, + "pass_count": 21, + "warn_count": 0, + "missing_count": 0, + "extension_track_count": 4, + "extension_partial_count": 0, + "extension_planned_count": 0, + "extension_covered_count": 4, + "adaptive_extension_ready": true, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_evidence_pending_count": 4, + "decision": "local-blueprint-covered-evidence-pending" + }, + "status_counts": { + "pass": 21, + "warn": 0, + "missing": 0 + }, + "extension_status_counts": { + "covered": 4 + }, + "modules": [ + { + "key": "skill-ir", + "category": "core-module", + "label": "Skill IR", + "status": "pass", + "objective": "Platform-neutral capability contract exists before platform-specific packaging.", + "current": "schema 2.0.0; targets 5", + "command": "python3 scripts/yao.py skill-ir .", + "test": "python3 tests/verify_skill_ir.py", + "evidence": [ + { + "path": "skill-ir/schema.json", + "exists": true + }, + { + "path": "skill-ir/examples/yao-meta-skill.json", + "exists": true + }, + { + "path": "scripts/export_skill_ir.py", + "exists": true + }, + { + "path": "tests/verify_skill_ir.py", + "exists": true + } + ], + "next_action": "Keep all target packages compiled from IR rather than hand-maintained per target." + }, + { + "key": "output-eval-lab", + "category": "core-module", + "label": "Output Eval Lab", + "status": "pass", + "objective": "with-skill/baseline output quality is measured with assertions, execution evidence, and blind review packs.", + "current": "5 cases; delta 100.0; execution 10", + "command": "python3 scripts/yao.py output-exec . && python3 scripts/yao.py output-review .", + "test": "python3 tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "evals/output/schema.json", + "exists": true + }, + { + "path": "evals/output/cases.jsonl", + "exists": true + }, + { + "path": "scripts/run_output_eval.py", + "exists": true + }, + { + "path": "scripts/run_output_execution.py", + "exists": true + }, + { + "path": "reports/output_quality_scorecard.json", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Add more real-file and adversarial holdout cases as adoption data grows." + }, + { + "key": "runtime-conformance", + "category": "core-module", + "label": "Runtime Conformance", + "status": "pass", + "objective": "Target packages can be consumed by OpenAI, Claude, Agent Skills, VS Code, and generic targets.", + "current": "5/5 targets pass", + "command": "python3 scripts/yao.py conformance .", + "test": "python3 tests/verify_conformance_suite.py", + "evidence": [ + { + "path": "runtime/conformance/schema.json", + "exists": true + }, + { + "path": "scripts/run_conformance_suite.py", + "exists": true + }, + { + "path": "reports/conformance_matrix.json", + "exists": true + }, + { + "path": "tests/verify_conformance_suite.py", + "exists": true + } + ], + "next_action": "Keep conformance cases aligned with current platform metadata rules." + }, + { + "key": "trust-security", + "category": "core-module", + "label": "Trust Security", + "status": "pass", + "objective": "Scripts, dependencies, permissions, secrets, and package hash are reviewable for team distribution.", + "current": "155 scripts; secrets 0; help failures 0", + "command": "python3 scripts/yao.py trust .", + "test": "python3 tests/verify_trust_check.py", + "evidence": [ + { + "path": "scripts/trust_check.py", + "exists": true + }, + { + "path": "security/trust_policy.md", + "exists": true + }, + { + "path": "security/script_policy.md", + "exists": true + }, + { + "path": "security/permission_policy.json", + "exists": true + }, + { + "path": "reports/security_trust_report.json", + "exists": true + }, + { + "path": "tests/verify_trust_check.py", + "exists": true + } + ], + "next_action": "Keep high-permission approvals scoped, expiring, and mapped to target enforcement." + }, + { + "key": "skill-atlas", + "category": "core-module", + "label": "Skill Atlas", + "status": "pass", + "objective": "Team skill portfolio reveals route collisions, stale ownership, dependency graph, and no-route opportunities.", + "current": "11 scanned skills; actionable collisions 0", + "command": "python3 scripts/yao.py skill-atlas --workspace-root .", + "test": "python3 tests/verify_skill_atlas.py", + "evidence": [ + { + "path": "scripts/build_skill_atlas.py", + "exists": true + }, + { + "path": "skill_atlas/catalog.json", + "exists": true + }, + { + "path": "skill_atlas/route_overlap_matrix.csv", + "exists": true + }, + { + "path": "skill_atlas/dependency_graph.json", + "exists": true + }, + { + "path": "reports/skill_atlas.json", + "exists": true + }, + { + "path": "tests/verify_skill_atlas.py", + "exists": true + } + ], + "next_action": "Use real telemetry to rank stale or drifting skills by impact, not only by static metadata." + }, + { + "key": "registry-distribution", + "category": "core-module", + "label": "Registry Distribution", + "status": "pass", + "objective": "Skill packages are installable, versioned, checksumed, and upgrade-reviewable.", + "current": "archive entries 580; install failures 0", + "command": "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip && python3 scripts/yao.py registry-audit .", + "test": "python3 tests/verify_registry_audit.py", + "evidence": [ + { + "path": "registry/index.schema.json", + "exists": true + }, + { + "path": "registry/package.schema.json", + "exists": true + }, + { + "path": "registry/packages/yao-meta-skill.json", + "exists": true + }, + { + "path": "scripts/registry_audit.py", + "exists": true + }, + { + "path": "reports/package_verification.json", + "exists": true + }, + { + "path": "reports/install_simulation.json", + "exists": true + }, + { + "path": "tests/verify_registry_audit.py", + "exists": true + } + ], + "next_action": "Regenerate registry metadata after package verification so source and archive checksums stay aligned." + }, + { + "key": "review-studio", + "category": "core-module", + "label": "Review Studio", + "status": "pass", + "objective": "One HTML page supports first-pass production review across trigger, output, runtime, trust, release, and evidence actions.", + "current": "16 gates; decision review; warnings 3", + "command": "python3 scripts/yao.py review-studio .", + "test": "python3 tests/verify_review_studio.py", + "evidence": [ + { + "path": "scripts/render_review_studio.py", + "exists": true + }, + { + "path": "reports/review-studio.html", + "exists": true + }, + { + "path": "reports/review-studio.json", + "exists": true + }, + { + "path": "tests/verify_review_studio.py", + "exists": true + } + ], + "next_action": "Close pending human and external evidence before claiming full release readiness." + }, + { + "key": "telemetry-drift", + "category": "core-module", + "label": "Telemetry Drift", + "status": "pass", + "objective": "Real usage feedback is captured as metadata-only local-first drift signals.", + "current": "events 1; recipes 5; risk low", + "command": "python3 scripts/yao.py telemetry-hooks . && python3 scripts/yao.py adoption-drift .", + "test": "python3 tests/verify_telemetry_hooks.py", + "evidence": [ + { + "path": "scripts/emit_telemetry_event.py", + "exists": true + }, + { + "path": "scripts/import_telemetry_events.py", + "exists": true + }, + { + "path": "scripts/telemetry_native_host.py", + "exists": true + }, + { + "path": "reports/adoption_drift_report.json", + "exists": true + }, + { + "path": "reports/telemetry_hook_recipes.json", + "exists": true + }, + { + "path": "tests/verify_telemetry_hooks.py", + "exists": true + } + ], + "next_action": "Install a real client and import production metadata-only events into the local drift loop." + } + ], + "recommended_prs": [ + { + "key": "benchmark-methodology", + "category": "recommended-pr", + "label": "Benchmark Methodology", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "25 required artifacts checked", + "command": "make ci-test", + "test": "tests/verify_benchmark_reproducibility.py", + "evidence": [ + { + "path": "reports/benchmark_methodology.md", + "exists": true + }, + { + "path": "reports/benchmark_reproducibility.json", + "exists": true + }, + { + "path": "tests/verify_benchmark_reproducibility.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "output-eval-schema", + "category": "recommended-pr", + "label": "Output Eval Schema", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "5 output cases", + "command": "make ci-test", + "test": "tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "evals/output/schema.json", + "exists": true + }, + { + "path": "evals/output/cases.jsonl", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "output-eval-runner", + "category": "recommended-pr", + "label": "Output Eval Runner", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "delta 100.0", + "command": "make ci-test", + "test": "tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "scripts/run_output_eval.py", + "exists": true + }, + { + "path": "reports/output_quality_scorecard.json", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "output-quality-scorecard", + "category": "recommended-pr", + "label": "Output Quality Scorecard", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "gate pass True", + "command": "make ci-test", + "test": "tests/verify_output_eval_lab.py", + "evidence": [ + { + "path": "reports/output_quality_scorecard.md", + "exists": true + }, + { + "path": "reports/output_quality_scorecard.json", + "exists": true + }, + { + "path": "tests/verify_output_eval_lab.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "skill-ir-v0", + "category": "recommended-pr", + "label": "Skill IR V0", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "schema 2.0.0", + "command": "make ci-test", + "test": "tests/verify_skill_ir.py", + "evidence": [ + { + "path": "skill-ir/schema.json", + "exists": true + }, + { + "path": "skill-ir/examples/yao-meta-skill.json", + "exists": true + }, + { + "path": "tests/verify_skill_ir.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "compiler-refactor", + "category": "recommended-pr", + "label": "Compiler Refactor", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "5/5 compiled targets", + "command": "make ci-test", + "test": "tests/verify_compile_skill.py", + "evidence": [ + { + "path": "scripts/compile_skill.py", + "exists": true + }, + { + "path": "reports/compiled_targets.json", + "exists": true + }, + { + "path": "tests/verify_compile_skill.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "agent-skills-conformance", + "category": "recommended-pr", + "label": "Agent Skills Conformance", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "agent-skills target present", + "command": "make ci-test", + "test": "tests/verify_conformance_suite.py", + "evidence": [ + { + "path": "runtime/conformance/schema.json", + "exists": true + }, + { + "path": "reports/conformance_matrix.json", + "exists": true + }, + { + "path": "tests/verify_conformance_suite.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "trust-check", + "category": "recommended-pr", + "label": "Trust Check", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "secret findings 0", + "command": "make ci-test", + "test": "tests/verify_trust_check.py", + "evidence": [ + { + "path": "scripts/trust_check.py", + "exists": true + }, + { + "path": "reports/security_trust_report.json", + "exists": true + }, + { + "path": "tests/verify_trust_check.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "skill-atlas-generator", + "category": "recommended-pr", + "label": "Skill Atlas Generator", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "11 scanned skills", + "command": "make ci-test", + "test": "tests/verify_skill_atlas.py", + "evidence": [ + { + "path": "scripts/build_skill_atlas.py", + "exists": true + }, + { + "path": "reports/skill_atlas.json", + "exists": true + }, + { + "path": "tests/verify_skill_atlas.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "registry-package-format", + "category": "recommended-pr", + "label": "Registry Package Format", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "registry ok True", + "command": "make ci-test", + "test": "tests/verify_registry_audit.py", + "evidence": [ + { + "path": "registry/package.schema.json", + "exists": true + }, + { + "path": "reports/registry_audit.json", + "exists": true + }, + { + "path": "tests/verify_registry_audit.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "review-studio-2", + "category": "recommended-pr", + "label": "Review Studio 2.0", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "16 review gates", + "command": "make ci-test", + "test": "tests/verify_review_studio.py", + "evidence": [ + { + "path": "scripts/render_review_studio.py", + "exists": true + }, + { + "path": "reports/review-studio.html", + "exists": true + }, + { + "path": "tests/verify_review_studio.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "migration-v2-docs", + "category": "recommended-pr", + "label": "Migration V2 Docs", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "migration guide present", + "command": "make ci-test", + "test": "docs review", + "evidence": [ + { + "path": "docs/migration-v2.md", + "exists": true + }, + { + "path": "reports/skill-os-2-review.md", + "exists": true + }, + { + "path": "README.md", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + }, + { + "key": "evidence-consistency", + "category": "recommended-pr", + "label": "Evidence Consistency", + "status": "pass", + "objective": "Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + "current": "39 consistency checks", + "command": "make ci-test", + "test": "tests/verify_evidence_consistency.py", + "evidence": [ + { + "path": "scripts/render_evidence_consistency.py", + "exists": true + }, + { + "path": "reports/evidence_consistency.json", + "exists": true + }, + { + "path": "tests/verify_evidence_consistency.py", + "exists": true + } + ], + "next_action": "Keep this item covered as the implementation evolves." + } + ], + "reference_extension_tracks": [ + { + "key": "skill-interpretation-report", + "label": "Skill Interpretation Report", + "status": "covered", + "objective": "User-facing deep interpretation report explains use cases, triggers, inputs, outputs, workflow, principles, boundaries, quality gates, examples, and next iterations.", + "current": "Skill Overview v2 is canonical and mirrored as first-class skill-interpretation HTML/JSON with schema and tests.", + "target": "Either keep skill-overview as the canonical interpretation report with an explicit contract, or split a dedicated reports/skill-interpretation.* renderer and tests.", + "evidence": [ + { + "path": "reports/skill-overview.html", + "exists": true + }, + { + "path": "reports/skill-overview.json", + "exists": true + }, + { + "path": "scripts/render_skill_overview.py", + "exists": true + }, + { + "path": "scripts/render_skill_interpretation.py", + "exists": true + }, + { + "path": "schemas/skill-interpretation.schema.json", + "exists": true + }, + { + "path": "tests/verify_skill_interpretation.py", + "exists": true + } + ], + "next_action": "Keep overview and interpretation contracts in lockstep when report sections, metrics, or layout semantics change." + }, + { + "key": "adaptive-self-iteration", + "label": "Adaptive Self-Iteration", + "status": "covered", + "objective": "Local-first preference memory, repeated-signal extraction, adaptation proposals, approval, patch application, regression evidence, and rollback.", + "current": "Full adaptive loop includes proposal, approval, patch application, regression evidence, and rollback metadata.", + "target": "Proposal-only adaptation with explicit input source, redaction, allowlisted write targets, approval ledger, regression report, and rollback plan.", + "evidence": [ + { + "path": "references/autonomous-adaptation.md", + "exists": true + }, + { + "path": "references/user-memory-policy.md", + "exists": true + }, + { + "path": "schemas/adaptation-proposal.schema.json", + "exists": true + }, + { + "path": "scripts/summarize_user_signals.py", + "exists": true + }, + { + "path": "scripts/propose_adaptation.py", + "exists": true + }, + { + "path": "tests/verify_adaptation_safety.py", + "exists": true + }, + { + "path": "scripts/apply_adaptation.py", + "exists": true + }, + { + "path": "reports/adaptation_approval_ledger.json", + "exists": true + }, + { + "path": "reports/adaptation_regression_report.json", + "exists": true + }, + { + "path": "reports/user_patterns.json", + "exists": true + }, + { + "path": "reports/adaptation_proposals.json", + "exists": true + }, + { + "path": "reports/iteration-directions.md", + "exists": true + }, + { + "path": "reports/adoption_drift_report.md", + "exists": true + } + ], + "next_action": "Start with policy and read-only scan tests; do not read shell history or private logs unless the user provides an explicit source path." + }, + { + "key": "daily-skillops-report", + "label": "Daily SkillOps Report", + "status": "covered", + "objective": "Daily operations layer summarizes explicit-source conversation patterns, proposal-only adaptation work, approval state, release locks, and world-class evidence gaps.", + "current": "Daily SkillOps report is a CLI-backed, explicit-source operations cockpit with tests and committed dated evidence.", + "target": "Generate reports/skillops/daily/YYYY-MM-DD.* from explicit sources without private log scanning, source writes, auto-patching, or world-class overclaiming.", + "evidence": [ + { + "path": "scripts/render_daily_skillops_report.py", + "exists": true + }, + { + "path": "tests/verify_daily_skillops.py", + "exists": true + }, + { + "path": "reports/skillops/daily/2026-07-02.json", + "exists": true + }, + { + "path": "reports/skillops/daily/2026-07-02.md", + "exists": true + } + ], + "next_action": "Keep Daily SkillOps report aligned with proposal, approval, coverage, and world-class ledger contracts as the operations layer evolves." + }, + { + "key": "weekly-curator-report", + "label": "Weekly Curator Report", + "status": "covered", + "objective": "Weekly curator layer aggregates Daily SkillOps opportunities, Skill Atlas portfolio signals, release locks, and world-class evidence gaps into a maintenance queue.", + "current": "Weekly curator report is CLI-backed, proposal-only, and backed by committed weekly JSON/Markdown evidence.", + "target": "Generate reports/skillops/weekly/YYYY-WNN.* without private log scanning, source writes, auto-patching, or world-class overclaiming.", + "evidence": [ + { + "path": "scripts/render_weekly_curator_report.py", + "exists": true + }, + { + "path": "tests/verify_weekly_curator.py", + "exists": true + }, + { + "path": "reports/skillops/weekly/2026-W27.json", + "exists": true + }, + { + "path": "reports/skillops/weekly/2026-W27.md", + "exists": true + } + ], + "next_action": "Use weekly curator output as the Skill Librarian maintenance queue before approving any durable skill-library changes." + } + ], + "next_highest_leverage": [ + "Close the four world-class evidence ledger entries with accepted human or external evidence.", + "Keep the first-class skill interpretation report and Skill Overview v2 contract synchronized as the report model evolves.", + "Use Daily SkillOps with explicit curated sources to review adaptation proposals without scanning private logs or auto-patching.", + "Keep the blueprint coverage report in CI so 2.0 plan drift is visible." + ], + "source_blueprint": { + "title": "Skill Overview / Skill OS 2.0 upgrade plan", + "core_module_count": 8, + "recommended_pr_count": 13, + "reference_extension_count": 4, + "reference_extensions": [ + "Skill interpretation report", + "Adaptive self-iteration with local preference memory and approval gates", + "Daily SkillOps operations report" + ] + }, + "artifacts": { + "json": "reports/skill_os2_coverage.json", + "markdown": "reports/skill_os2_coverage.md" + } +} diff --git a/reports/skill_os2_coverage.md b/reports/skill_os2_coverage.md new file mode 100644 index 0000000..fd2a513 --- /dev/null +++ b/reports/skill_os2_coverage.md @@ -0,0 +1,246 @@ +# Skill OS 2.0 Blueprint Coverage + +Generated at: `2026-07-02` + +## Summary + +- decision: `local-blueprint-covered-evidence-pending` +- local blueprint ready: `true` +- public world-class ready: `false` +- pass: `21` / `21` +- missing: `0` +- warn: `0` +- reference extensions: `4` +- extension covered: `4` +- extension partial: `0` +- extension planned: `0` +- adaptive extension ready: `true` +- world-class evidence pending: `4` + +This report maps the Skill OS 2.0 upgrade blueprint to concrete local artifacts, commands, and tests. It does not count pending human review, provider runs, metadata fallbacks, or planned work as public world-class evidence. + +## Core Modules + +| Item | Status | Current | Command | Test | +| --- | --- | --- | --- | --- | +| Skill IR | `pass` | schema 2.0.0; targets 5 | `python3 scripts/yao.py skill-ir .` | `python3 tests/verify_skill_ir.py` | +| Output Eval Lab | `pass` | 5 cases; delta 100.0; execution 10 | `python3 scripts/yao.py output-exec . && python3 scripts/yao.py output-review .` | `python3 tests/verify_output_eval_lab.py` | +| Runtime Conformance | `pass` | 5/5 targets pass | `python3 scripts/yao.py conformance .` | `python3 tests/verify_conformance_suite.py` | +| Trust Security | `pass` | 155 scripts; secrets 0; help failures 0 | `python3 scripts/yao.py trust .` | `python3 tests/verify_trust_check.py` | +| Skill Atlas | `pass` | 11 scanned skills; actionable collisions 0 | `python3 scripts/yao.py skill-atlas --workspace-root .` | `python3 tests/verify_skill_atlas.py` | +| Registry Distribution | `pass` | archive entries 580; install failures 0 | `python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip && python3 scripts/yao.py registry-audit .` | `python3 tests/verify_registry_audit.py` | +| Review Studio | `pass` | 16 gates; decision review; warnings 3 | `python3 scripts/yao.py review-studio .` | `python3 tests/verify_review_studio.py` | +| Telemetry Drift | `pass` | events 1; recipes 5; risk low | `python3 scripts/yao.py telemetry-hooks . && python3 scripts/yao.py adoption-drift .` | `python3 tests/verify_telemetry_hooks.py` | + +## Recommended PR Coverage + +| Item | Status | Current | Command | Test | +| --- | --- | --- | --- | --- | +| Benchmark Methodology | `pass` | 25 required artifacts checked | `make ci-test` | `tests/verify_benchmark_reproducibility.py` | +| Output Eval Schema | `pass` | 5 output cases | `make ci-test` | `tests/verify_output_eval_lab.py` | +| Output Eval Runner | `pass` | delta 100.0 | `make ci-test` | `tests/verify_output_eval_lab.py` | +| Output Quality Scorecard | `pass` | gate pass True | `make ci-test` | `tests/verify_output_eval_lab.py` | +| Skill IR V0 | `pass` | schema 2.0.0 | `make ci-test` | `tests/verify_skill_ir.py` | +| Compiler Refactor | `pass` | 5/5 compiled targets | `make ci-test` | `tests/verify_compile_skill.py` | +| Agent Skills Conformance | `pass` | agent-skills target present | `make ci-test` | `tests/verify_conformance_suite.py` | +| Trust Check | `pass` | secret findings 0 | `make ci-test` | `tests/verify_trust_check.py` | +| Skill Atlas Generator | `pass` | 11 scanned skills | `make ci-test` | `tests/verify_skill_atlas.py` | +| Registry Package Format | `pass` | registry ok True | `make ci-test` | `tests/verify_registry_audit.py` | +| Review Studio 2.0 | `pass` | 16 review gates | `make ci-test` | `tests/verify_review_studio.py` | +| Migration V2 Docs | `pass` | migration guide present | `make ci-test` | `docs review` | +| Evidence Consistency | `pass` | 39 consistency checks | `make ci-test` | `tests/verify_evidence_consistency.py` | + +## Reference Extension Tracks + +| Track | Status | Current | Target | Next action | +| --- | --- | --- | --- | --- | +| Skill Interpretation Report | `covered` | Skill Overview v2 is canonical and mirrored as first-class skill-interpretation HTML/JSON with schema and tests. | Either keep skill-overview as the canonical interpretation report with an explicit contract, or split a dedicated reports/skill-interpretation.* renderer and tests. | Keep overview and interpretation contracts in lockstep when report sections, metrics, or layout semantics change. | +| Adaptive Self-Iteration | `covered` | Full adaptive loop includes proposal, approval, patch application, regression evidence, and rollback metadata. | Proposal-only adaptation with explicit input source, redaction, allowlisted write targets, approval ledger, regression report, and rollback plan. | Start with policy and read-only scan tests; do not read shell history or private logs unless the user provides an explicit source path. | +| Daily SkillOps Report | `covered` | Daily SkillOps report is a CLI-backed, explicit-source operations cockpit with tests and committed dated evidence. | Generate reports/skillops/daily/YYYY-MM-DD.* from explicit sources without private log scanning, source writes, auto-patching, or world-class overclaiming. | Keep Daily SkillOps report aligned with proposal, approval, coverage, and world-class ledger contracts as the operations layer evolves. | +| Weekly Curator Report | `covered` | Weekly curator report is CLI-backed, proposal-only, and backed by committed weekly JSON/Markdown evidence. | Generate reports/skillops/weekly/YYYY-WNN.* without private log scanning, source writes, auto-patching, or world-class overclaiming. | Use weekly curator output as the Skill Librarian maintenance queue before approving any durable skill-library changes. | + +These extension tracks come from the user-supplied 2.0 reference plan. They are tracked separately from the formal Skill OS blueprint so the report can distinguish landed local architecture from planned explainer/adaptor evolution. + +## Next Highest-Leverage Moves + +- Close the four world-class evidence ledger entries with accepted human or external evidence. +- Keep the first-class skill interpretation report and Skill Overview v2 contract synchronized as the report model evolves. +- Use Daily SkillOps with explicit curated sources to review adaptation proposals without scanning private logs or auto-patching. +- Keep the blueprint coverage report in CI so 2.0 plan drift is visible. + +## Evidence Detail + +### Skill IR + +- objective: Platform-neutral capability contract exists before platform-specific packaging. +- status: `pass` +- existing evidence: `skill-ir/schema.json`, `skill-ir/examples/yao-meta-skill.json`, `scripts/export_skill_ir.py`, `tests/verify_skill_ir.py` +- next action: Keep all target packages compiled from IR rather than hand-maintained per target. + +### Output Eval Lab + +- objective: with-skill/baseline output quality is measured with assertions, execution evidence, and blind review packs. +- status: `pass` +- existing evidence: `evals/output/schema.json`, `evals/output/cases.jsonl`, `scripts/run_output_eval.py`, `scripts/run_output_execution.py`, `reports/output_quality_scorecard.json`, `tests/verify_output_eval_lab.py` +- next action: Add more real-file and adversarial holdout cases as adoption data grows. + +### Runtime Conformance + +- objective: Target packages can be consumed by OpenAI, Claude, Agent Skills, VS Code, and generic targets. +- status: `pass` +- existing evidence: `runtime/conformance/schema.json`, `scripts/run_conformance_suite.py`, `reports/conformance_matrix.json`, `tests/verify_conformance_suite.py` +- next action: Keep conformance cases aligned with current platform metadata rules. + +### Trust Security + +- objective: Scripts, dependencies, permissions, secrets, and package hash are reviewable for team distribution. +- status: `pass` +- existing evidence: `scripts/trust_check.py`, `security/trust_policy.md`, `security/script_policy.md`, `security/permission_policy.json`, `reports/security_trust_report.json`, `tests/verify_trust_check.py` +- next action: Keep high-permission approvals scoped, expiring, and mapped to target enforcement. + +### Skill Atlas + +- objective: Team skill portfolio reveals route collisions, stale ownership, dependency graph, and no-route opportunities. +- status: `pass` +- existing evidence: `scripts/build_skill_atlas.py`, `skill_atlas/catalog.json`, `skill_atlas/route_overlap_matrix.csv`, `skill_atlas/dependency_graph.json`, `reports/skill_atlas.json`, `tests/verify_skill_atlas.py` +- next action: Use real telemetry to rank stale or drifting skills by impact, not only by static metadata. + +### Registry Distribution + +- objective: Skill packages are installable, versioned, checksumed, and upgrade-reviewable. +- status: `pass` +- existing evidence: `registry/index.schema.json`, `registry/package.schema.json`, `registry/packages/yao-meta-skill.json`, `scripts/registry_audit.py`, `reports/package_verification.json`, `reports/install_simulation.json`, `tests/verify_registry_audit.py` +- next action: Regenerate registry metadata after package verification so source and archive checksums stay aligned. + +### Review Studio + +- objective: One HTML page supports first-pass production review across trigger, output, runtime, trust, release, and evidence actions. +- status: `pass` +- existing evidence: `scripts/render_review_studio.py`, `reports/review-studio.html`, `reports/review-studio.json`, `tests/verify_review_studio.py` +- next action: Close pending human and external evidence before claiming full release readiness. + +### Telemetry Drift + +- objective: Real usage feedback is captured as metadata-only local-first drift signals. +- status: `pass` +- existing evidence: `scripts/emit_telemetry_event.py`, `scripts/import_telemetry_events.py`, `scripts/telemetry_native_host.py`, `reports/adoption_drift_report.json`, `reports/telemetry_hook_recipes.json`, `tests/verify_telemetry_hooks.py` +- next action: Install a real client and import production metadata-only events into the local drift loop. + +### Benchmark Methodology + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `reports/benchmark_methodology.md`, `reports/benchmark_reproducibility.json`, `tests/verify_benchmark_reproducibility.py` +- next action: Keep this item covered as the implementation evolves. + +### Output Eval Schema + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `evals/output/schema.json`, `evals/output/cases.jsonl`, `tests/verify_output_eval_lab.py` +- next action: Keep this item covered as the implementation evolves. + +### Output Eval Runner + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `scripts/run_output_eval.py`, `reports/output_quality_scorecard.json`, `tests/verify_output_eval_lab.py` +- next action: Keep this item covered as the implementation evolves. + +### Output Quality Scorecard + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `reports/output_quality_scorecard.md`, `reports/output_quality_scorecard.json`, `tests/verify_output_eval_lab.py` +- next action: Keep this item covered as the implementation evolves. + +### Skill IR V0 + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `skill-ir/schema.json`, `skill-ir/examples/yao-meta-skill.json`, `tests/verify_skill_ir.py` +- next action: Keep this item covered as the implementation evolves. + +### Compiler Refactor + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `scripts/compile_skill.py`, `reports/compiled_targets.json`, `tests/verify_compile_skill.py` +- next action: Keep this item covered as the implementation evolves. + +### Agent Skills Conformance + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `runtime/conformance/schema.json`, `reports/conformance_matrix.json`, `tests/verify_conformance_suite.py` +- next action: Keep this item covered as the implementation evolves. + +### Trust Check + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `scripts/trust_check.py`, `reports/security_trust_report.json`, `tests/verify_trust_check.py` +- next action: Keep this item covered as the implementation evolves. + +### Skill Atlas Generator + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `scripts/build_skill_atlas.py`, `reports/skill_atlas.json`, `tests/verify_skill_atlas.py` +- next action: Keep this item covered as the implementation evolves. + +### Registry Package Format + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `registry/package.schema.json`, `reports/registry_audit.json`, `tests/verify_registry_audit.py` +- next action: Keep this item covered as the implementation evolves. + +### Review Studio 2.0 + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `scripts/render_review_studio.py`, `reports/review-studio.html`, `tests/verify_review_studio.py` +- next action: Keep this item covered as the implementation evolves. + +### Migration V2 Docs + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `docs/migration-v2.md`, `reports/skill-os-2-review.md`, `README.md` +- next action: Keep this item covered as the implementation evolves. + +### Evidence Consistency + +- objective: Recommended Skill OS 2.0 implementation PR from the upgrade plan. +- status: `pass` +- existing evidence: `scripts/render_evidence_consistency.py`, `reports/evidence_consistency.json`, `tests/verify_evidence_consistency.py` +- next action: Keep this item covered as the implementation evolves. + +### Skill Interpretation Report + +- objective: User-facing deep interpretation report explains use cases, triggers, inputs, outputs, workflow, principles, boundaries, quality gates, examples, and next iterations. +- status: `covered` +- existing evidence: `reports/skill-overview.html`, `reports/skill-overview.json`, `scripts/render_skill_overview.py`, `scripts/render_skill_interpretation.py`, `schemas/skill-interpretation.schema.json`, `tests/verify_skill_interpretation.py` +- next action: Keep overview and interpretation contracts in lockstep when report sections, metrics, or layout semantics change. + +### Adaptive Self-Iteration + +- objective: Local-first preference memory, repeated-signal extraction, adaptation proposals, approval, patch application, regression evidence, and rollback. +- status: `covered` +- existing evidence: `references/autonomous-adaptation.md`, `references/user-memory-policy.md`, `schemas/adaptation-proposal.schema.json`, `scripts/summarize_user_signals.py`, `scripts/propose_adaptation.py`, `tests/verify_adaptation_safety.py`, `scripts/apply_adaptation.py`, `reports/adaptation_approval_ledger.json`, `reports/adaptation_regression_report.json`, `reports/user_patterns.json`, `reports/adaptation_proposals.json`, `reports/iteration-directions.md`, `reports/adoption_drift_report.md` +- next action: Start with policy and read-only scan tests; do not read shell history or private logs unless the user provides an explicit source path. + +### Daily SkillOps Report + +- objective: Daily operations layer summarizes explicit-source conversation patterns, proposal-only adaptation work, approval state, release locks, and world-class evidence gaps. +- status: `covered` +- existing evidence: `scripts/render_daily_skillops_report.py`, `tests/verify_daily_skillops.py`, `reports/skillops/daily/2026-07-02.json`, `reports/skillops/daily/2026-07-02.md` +- next action: Keep Daily SkillOps report aligned with proposal, approval, coverage, and world-class ledger contracts as the operations layer evolves. + +### Weekly Curator Report + +- objective: Weekly curator layer aggregates Daily SkillOps opportunities, Skill Atlas portfolio signals, release locks, and world-class evidence gaps into a maintenance queue. +- status: `covered` +- existing evidence: `scripts/render_weekly_curator_report.py`, `tests/verify_weekly_curator.py`, `reports/skillops/weekly/2026-W27.json`, `reports/skillops/weekly/2026-W27.md` +- next action: Use weekly curator output as the Skill Librarian maintenance queue before approving any durable skill-library changes. diff --git a/reports/skillops/daily/2026-06-16.json b/reports/skillops/daily/2026-06-16.json new file mode 100644 index 0000000..331b783 --- /dev/null +++ b/reports/skillops/daily/2026-06-16.json @@ -0,0 +1,513 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-16T21:24:27Z", + "skill_dir": ".", + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "operations_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "explicit_source_required_for_scan": true, + "implicit_private_log_scan": false, + "raw_content_stored": false, + "redacted_excerpts_only": true, + "proposal_only": true, + "approval_required_for_writes": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "daily_report_counts_as_world_class_evidence": false + }, + "report_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "source_supplied", + "pattern_count", + "proposal_count", + "approval_count", + "pending_review_count", + "applied_count", + "rollback_count", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_pending_count", + "release_lock_ready", + "evidence_consistency_ok", + "writes_source_files", + "auto_patch_enabled", + "failure_count" + ], + "source_of_truth": [ + "summary", + "operations_contract" + ] + }, + "source": { + "path": "evals/adaptation/user_signals.example.jsonl", + "fingerprint_sha256": "48f94a4f3b82f90db29921c13d09c765ec10aabfde1adce65710c137cc6c5abf", + "record_count": 8, + "explicit_source": true, + "raw_content_stored": false, + "redacted_excerpts_only": true + }, + "patterns": [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated report-language signals.", + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "support_count": 5, + "confidence": 0.95, + "reason": "5 redacted records matched repeated artifact-design signals.", + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated governance signals.", + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + "evidence": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated artifact-format signals.", + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + "evidence": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated quality-gate signals.", + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + "evidence": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "opportunity_summary": { + "opportunity_count": 5, + "top_score": 88, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "action_type_counts": { + "patch_existing_skill": 3, + "agents_update": 1, + "add_eval": 1 + }, + "decision_counts": { + "ready_for_approval_review": 1, + "proposal_review": 4 + } + }, + "opportunities": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "decision_policy": { + "schema_version": "1.0", + "contract": "skillops-opportunity-scoring", + "score_range": [ + 0, + 100 + ], + "score_bands": { + "85-100": "ready_for_approval_review", + "70-84": "proposal_review", + "50-69": "observe_more_evidence", + "0-49": "report_only_or_no_action" + }, + "action_types": [ + "add_eval", + "agents_update", + "patch_existing_skill", + "report_only" + ], + "writes_source_files": false, + "auto_patch_enabled": false, + "approval_required_for_writes": true + }, + "approval": { + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "release_state": { + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true + }, + "actions": [ + { + "key": "review-adaptation-proposals", + "priority": "high", + "action": "Review proposal-only adaptation items before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for the pending world-class ledger entries." + } + ], + "failures": [], + "source_reports": { + "patterns": "reports/user_patterns.json", + "proposals": "reports/adaptation_proposals.json", + "approval_ledger": "reports/adaptation_approval_ledger.json", + "regression": "reports/adaptation_regression_report.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "evidence_consistency": "reports/evidence_consistency.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json" + }, + "artifacts": { + "json": "reports/skillops/daily/2026-06-16.json", + "markdown": "reports/skillops/daily/2026-06-16.md" + } +} diff --git a/reports/skillops/daily/2026-06-16.md b/reports/skillops/daily/2026-06-16.md new file mode 100644 index 0000000..5d1c83f --- /dev/null +++ b/reports/skillops/daily/2026-06-16.md @@ -0,0 +1,174 @@ +# Daily SkillOps Report + +Generated at: `2026-06-16T21:24:27Z` + +## Summary + +- decision: `proposal-review` +- source supplied: `false` +- patterns: `5` +- proposals: `5` +- pending approvals: `0` +- applied patches: `0` +- rollbacks: `0` +- local blueprint ready: `true` +- public world-class ready: `false` +- world-class pending: `4` +- release lock ready: `false` +- evidence consistency ok: `true` + +This report is an operations cockpit for explicit-source SkillOps. It does not scan private logs, write source files, apply patches, or count as world-class external or human evidence. + +## Privacy Boundary + +- explicit_source_required_for_scan: `true` +- implicit_private_log_scan: `false` +- raw_content_stored: `false` +- redacted_excerpts_only: `true` +- proposal_only: `true` +- approval_required_for_writes: `true` +- writes_source_files: `false` +- auto_patch_enabled: `false` +- daily_report_counts_as_world_class_evidence: `false` + +## Actions + +- `high` Review proposal-only adaptation items before preparing any approval ledger entry. +- `high` Collect accepted external or human evidence for the pending world-class ledger entries. + +## Opportunities + +- count: `5` +- top score: `88` +- ready for approval review: `1` +### Improve report layout, visual hierarchy, and chart readability + +- ID: `skillops-4ad91c18dd` +- Action: `patch_existing_skill` +- Decision: `ready_for_approval_review` +- Score: `88` +- Risk: `medium` +- Policy: Repeated report layout feedback maps to existing renderer and design doctrine changes. +### Keep adaptive iteration approval-gated + +- ID: `skillops-531e13c1c6` +- Action: `agents_update` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated privacy and approval signals should tighten durable operating guidance. +### Keep reports Chinese-first with optional English + +- ID: `skillops-709ef884f3` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated language preferences can improve existing report templates after approval. +### Make generated artifact paths explicit in CLI output + +- ID: `skillops-f9e43237aa` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated delivery-path requests can improve CLI and README artifact discoverability. +### Attach tests and evidence refresh to each upgrade + +- ID: `skillops-0a3a2de7c3` +- Action: `add_eval` +- Decision: `proposal_review` +- Score: `74` +- Risk: `medium` +- Policy: Repeated verification and evidence requests should become focused checks. + +## Patterns + +- `language_default`: Default language preference (support `2`, confidence `0.79`) +- `report_ui`: Report UI and visualization preference (support `5`, confidence `0.95`) +- `approval_safety`: Approval and privacy boundary (support `2`, confidence `0.79`) +- `delivery_format`: Delivery format preference (support `2`, confidence `0.79`) +- `evidence_testing`: Evidence and testing preference (support `2`, confidence `0.79`) + +## Proposals + +### Keep reports Chinese-first with optional English + +- ID: `adapt-18c7517f3d` +- Pattern: `language_default` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` +- Verification: + - `python3 tests/verify_skill_overview.py` + +### Improve report layout, visual hierarchy, and chart readability + +- ID: `adapt-fbfe921ba5` +- Pattern: `report_ui` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` + - `tests/verify_skill_overview.py` +- Verification: + - `python3 tests/verify_skill_overview.py` + - `python3 tests/verify_skill_report_charts.py` + +### Keep adaptive iteration approval-gated + +- ID: `adapt-59d219a1fb` +- Pattern: `approval_safety` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `references/user-memory-policy.md` + - `references/autonomous-adaptation.md` + - `schemas/adaptation-proposal.schema.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + +### Make generated artifact paths explicit in CLI output + +- ID: `adapt-457baca160` +- Pattern: `delivery_format` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/yao.py` + - `README.md` +- Verification: + - `python3 tests/verify_yao_cli.py` + +### Attach tests and evidence refresh to each upgrade + +- ID: `adapt-abfee25d3a` +- Pattern: `evidence_testing` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `tests/verify_adaptation_safety.py` + - `scripts/render_skill_os2_coverage.py` + - `reports/skill_os2_coverage.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + - `python3 tests/verify_skill_os2_coverage.py` + +## Evidence + +- patterns: `reports/user_patterns.json` +- proposals: `reports/adaptation_proposals.json` +- approval_ledger: `reports/adaptation_approval_ledger.json` +- regression: `reports/adaptation_regression_report.json` +- skill_os2_coverage: `reports/skill_os2_coverage.json` +- world_class_ledger: `reports/world_class_evidence_ledger.json` +- evidence_consistency: `reports/evidence_consistency.json` +- benchmark_reproducibility: `reports/benchmark_reproducibility.json` diff --git a/reports/skillops/daily/2026-06-17.json b/reports/skillops/daily/2026-06-17.json new file mode 100644 index 0000000..0e4493b --- /dev/null +++ b/reports/skillops/daily/2026-06-17.json @@ -0,0 +1,513 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-17T08:58:54Z", + "skill_dir": ".", + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "operations_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "explicit_source_required_for_scan": true, + "implicit_private_log_scan": false, + "raw_content_stored": false, + "redacted_excerpts_only": true, + "proposal_only": true, + "approval_required_for_writes": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "daily_report_counts_as_world_class_evidence": false + }, + "report_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "source_supplied", + "pattern_count", + "proposal_count", + "approval_count", + "pending_review_count", + "applied_count", + "rollback_count", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_pending_count", + "release_lock_ready", + "evidence_consistency_ok", + "writes_source_files", + "auto_patch_enabled", + "failure_count" + ], + "source_of_truth": [ + "summary", + "operations_contract" + ] + }, + "source": { + "path": "evals/adaptation/user_signals.example.jsonl", + "fingerprint_sha256": "48f94a4f3b82f90db29921c13d09c765ec10aabfde1adce65710c137cc6c5abf", + "record_count": 8, + "explicit_source": true, + "raw_content_stored": false, + "redacted_excerpts_only": true + }, + "patterns": [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated report-language signals.", + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "support_count": 5, + "confidence": 0.95, + "reason": "5 redacted records matched repeated artifact-design signals.", + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated governance signals.", + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + "evidence": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated artifact-format signals.", + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + "evidence": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated quality-gate signals.", + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + "evidence": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "opportunity_summary": { + "opportunity_count": 5, + "top_score": 88, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "action_type_counts": { + "patch_existing_skill": 3, + "agents_update": 1, + "add_eval": 1 + }, + "decision_counts": { + "ready_for_approval_review": 1, + "proposal_review": 4 + } + }, + "opportunities": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "decision_policy": { + "schema_version": "1.0", + "contract": "skillops-opportunity-scoring", + "score_range": [ + 0, + 100 + ], + "score_bands": { + "85-100": "ready_for_approval_review", + "70-84": "proposal_review", + "50-69": "observe_more_evidence", + "0-49": "report_only_or_no_action" + }, + "action_types": [ + "add_eval", + "agents_update", + "patch_existing_skill", + "report_only" + ], + "writes_source_files": false, + "auto_patch_enabled": false, + "approval_required_for_writes": true + }, + "approval": { + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "release_state": { + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true + }, + "actions": [ + { + "key": "review-adaptation-proposals", + "priority": "high", + "action": "Review proposal-only adaptation items before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for the pending world-class ledger entries." + } + ], + "failures": [], + "source_reports": { + "patterns": "reports/user_patterns.json", + "proposals": "reports/adaptation_proposals.json", + "approval_ledger": "reports/adaptation_approval_ledger.json", + "regression": "reports/adaptation_regression_report.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "evidence_consistency": "reports/evidence_consistency.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json" + }, + "artifacts": { + "json": "reports/skillops/daily/2026-06-17.json", + "markdown": "reports/skillops/daily/2026-06-17.md" + } +} diff --git a/reports/skillops/daily/2026-06-17.md b/reports/skillops/daily/2026-06-17.md new file mode 100644 index 0000000..628fd7c --- /dev/null +++ b/reports/skillops/daily/2026-06-17.md @@ -0,0 +1,174 @@ +# Daily SkillOps Report + +Generated at: `2026-06-17T08:58:54Z` + +## Summary + +- decision: `proposal-review` +- source supplied: `false` +- patterns: `5` +- proposals: `5` +- pending approvals: `0` +- applied patches: `0` +- rollbacks: `0` +- local blueprint ready: `true` +- public world-class ready: `false` +- world-class pending: `4` +- release lock ready: `false` +- evidence consistency ok: `true` + +This report is an operations cockpit for explicit-source SkillOps. It does not scan private logs, write source files, apply patches, or count as world-class external or human evidence. + +## Privacy Boundary + +- explicit_source_required_for_scan: `true` +- implicit_private_log_scan: `false` +- raw_content_stored: `false` +- redacted_excerpts_only: `true` +- proposal_only: `true` +- approval_required_for_writes: `true` +- writes_source_files: `false` +- auto_patch_enabled: `false` +- daily_report_counts_as_world_class_evidence: `false` + +## Actions + +- `high` Review proposal-only adaptation items before preparing any approval ledger entry. +- `high` Collect accepted external or human evidence for the pending world-class ledger entries. + +## Opportunities + +- count: `5` +- top score: `88` +- ready for approval review: `1` +### Improve report layout, visual hierarchy, and chart readability + +- ID: `skillops-4ad91c18dd` +- Action: `patch_existing_skill` +- Decision: `ready_for_approval_review` +- Score: `88` +- Risk: `medium` +- Policy: Repeated report layout feedback maps to existing renderer and design doctrine changes. +### Keep adaptive iteration approval-gated + +- ID: `skillops-531e13c1c6` +- Action: `agents_update` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated privacy and approval signals should tighten durable operating guidance. +### Keep reports Chinese-first with optional English + +- ID: `skillops-709ef884f3` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated language preferences can improve existing report templates after approval. +### Make generated artifact paths explicit in CLI output + +- ID: `skillops-f9e43237aa` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated delivery-path requests can improve CLI and README artifact discoverability. +### Attach tests and evidence refresh to each upgrade + +- ID: `skillops-0a3a2de7c3` +- Action: `add_eval` +- Decision: `proposal_review` +- Score: `74` +- Risk: `medium` +- Policy: Repeated verification and evidence requests should become focused checks. + +## Patterns + +- `language_default`: Default language preference (support `2`, confidence `0.79`) +- `report_ui`: Report UI and visualization preference (support `5`, confidence `0.95`) +- `approval_safety`: Approval and privacy boundary (support `2`, confidence `0.79`) +- `delivery_format`: Delivery format preference (support `2`, confidence `0.79`) +- `evidence_testing`: Evidence and testing preference (support `2`, confidence `0.79`) + +## Proposals + +### Keep reports Chinese-first with optional English + +- ID: `adapt-18c7517f3d` +- Pattern: `language_default` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` +- Verification: + - `python3 tests/verify_skill_overview.py` + +### Improve report layout, visual hierarchy, and chart readability + +- ID: `adapt-fbfe921ba5` +- Pattern: `report_ui` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` + - `tests/verify_skill_overview.py` +- Verification: + - `python3 tests/verify_skill_overview.py` + - `python3 tests/verify_skill_report_charts.py` + +### Keep adaptive iteration approval-gated + +- ID: `adapt-59d219a1fb` +- Pattern: `approval_safety` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `references/user-memory-policy.md` + - `references/autonomous-adaptation.md` + - `schemas/adaptation-proposal.schema.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + +### Make generated artifact paths explicit in CLI output + +- ID: `adapt-457baca160` +- Pattern: `delivery_format` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/yao.py` + - `README.md` +- Verification: + - `python3 tests/verify_yao_cli.py` + +### Attach tests and evidence refresh to each upgrade + +- ID: `adapt-abfee25d3a` +- Pattern: `evidence_testing` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `tests/verify_adaptation_safety.py` + - `scripts/render_skill_os2_coverage.py` + - `reports/skill_os2_coverage.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + - `python3 tests/verify_skill_os2_coverage.py` + +## Evidence + +- patterns: `reports/user_patterns.json` +- proposals: `reports/adaptation_proposals.json` +- approval_ledger: `reports/adaptation_approval_ledger.json` +- regression: `reports/adaptation_regression_report.json` +- skill_os2_coverage: `reports/skill_os2_coverage.json` +- world_class_ledger: `reports/world_class_evidence_ledger.json` +- evidence_consistency: `reports/evidence_consistency.json` +- benchmark_reproducibility: `reports/benchmark_reproducibility.json` diff --git a/reports/skillops/daily/2026-06-20.json b/reports/skillops/daily/2026-06-20.json new file mode 100644 index 0000000..46357b5 --- /dev/null +++ b/reports/skillops/daily/2026-06-20.json @@ -0,0 +1,513 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-20T12:35:26Z", + "skill_dir": ".", + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": true, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": true, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "operations_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "explicit_source_required_for_scan": true, + "implicit_private_log_scan": false, + "raw_content_stored": false, + "redacted_excerpts_only": true, + "proposal_only": true, + "approval_required_for_writes": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "daily_report_counts_as_world_class_evidence": false + }, + "report_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "source_supplied", + "pattern_count", + "proposal_count", + "approval_count", + "pending_review_count", + "applied_count", + "rollback_count", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_pending_count", + "release_lock_ready", + "evidence_consistency_ok", + "writes_source_files", + "auto_patch_enabled", + "failure_count" + ], + "source_of_truth": [ + "summary", + "operations_contract" + ] + }, + "source": { + "path": "evals/adaptation/user_signals.example.jsonl", + "fingerprint_sha256": "48f94a4f3b82f90db29921c13d09c765ec10aabfde1adce65710c137cc6c5abf", + "record_count": 8, + "explicit_source": true, + "raw_content_stored": false, + "redacted_excerpts_only": true + }, + "patterns": [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated report-language signals.", + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "support_count": 5, + "confidence": 0.95, + "reason": "5 redacted records matched repeated artifact-design signals.", + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated governance signals.", + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + "evidence": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated artifact-format signals.", + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + "evidence": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated quality-gate signals.", + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + "evidence": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "opportunity_summary": { + "opportunity_count": 5, + "top_score": 88, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "action_type_counts": { + "patch_existing_skill": 3, + "agents_update": 1, + "add_eval": 1 + }, + "decision_counts": { + "ready_for_approval_review": 1, + "proposal_review": 4 + } + }, + "opportunities": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "decision_policy": { + "schema_version": "1.0", + "contract": "skillops-opportunity-scoring", + "score_range": [ + 0, + 100 + ], + "score_bands": { + "85-100": "ready_for_approval_review", + "70-84": "proposal_review", + "50-69": "observe_more_evidence", + "0-49": "report_only_or_no_action" + }, + "action_types": [ + "add_eval", + "agents_update", + "patch_existing_skill", + "report_only" + ], + "writes_source_files": false, + "auto_patch_enabled": false, + "approval_required_for_writes": true + }, + "approval": { + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "release_state": { + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": true, + "evidence_consistency_ok": true + }, + "actions": [ + { + "key": "review-adaptation-proposals", + "priority": "high", + "action": "Review proposal-only adaptation items before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for the pending world-class ledger entries." + } + ], + "failures": [], + "source_reports": { + "patterns": "reports/user_patterns.json", + "proposals": "reports/adaptation_proposals.json", + "approval_ledger": "reports/adaptation_approval_ledger.json", + "regression": "reports/adaptation_regression_report.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "evidence_consistency": "reports/evidence_consistency.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json" + }, + "artifacts": { + "json": "reports/skillops/daily/2026-06-20.json", + "markdown": "reports/skillops/daily/2026-06-20.md" + } +} diff --git a/reports/skillops/daily/2026-06-20.md b/reports/skillops/daily/2026-06-20.md new file mode 100644 index 0000000..f83a755 --- /dev/null +++ b/reports/skillops/daily/2026-06-20.md @@ -0,0 +1,174 @@ +# Daily SkillOps Report + +Generated at: `2026-06-20T12:35:26Z` + +## Summary + +- decision: `proposal-review` +- source supplied: `false` +- patterns: `5` +- proposals: `5` +- pending approvals: `0` +- applied patches: `0` +- rollbacks: `0` +- local blueprint ready: `true` +- public world-class ready: `false` +- world-class pending: `4` +- release lock ready: `true` +- evidence consistency ok: `true` + +This report is an operations cockpit for explicit-source SkillOps. It does not scan private logs, write source files, apply patches, or count as world-class external or human evidence. + +## Privacy Boundary + +- explicit_source_required_for_scan: `true` +- implicit_private_log_scan: `false` +- raw_content_stored: `false` +- redacted_excerpts_only: `true` +- proposal_only: `true` +- approval_required_for_writes: `true` +- writes_source_files: `false` +- auto_patch_enabled: `false` +- daily_report_counts_as_world_class_evidence: `false` + +## Actions + +- `high` Review proposal-only adaptation items before preparing any approval ledger entry. +- `high` Collect accepted external or human evidence for the pending world-class ledger entries. + +## Opportunities + +- count: `5` +- top score: `88` +- ready for approval review: `1` +### Improve report layout, visual hierarchy, and chart readability + +- ID: `skillops-4ad91c18dd` +- Action: `patch_existing_skill` +- Decision: `ready_for_approval_review` +- Score: `88` +- Risk: `medium` +- Policy: Repeated report layout feedback maps to existing renderer and design doctrine changes. +### Keep adaptive iteration approval-gated + +- ID: `skillops-531e13c1c6` +- Action: `agents_update` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated privacy and approval signals should tighten durable operating guidance. +### Keep reports Chinese-first with optional English + +- ID: `skillops-709ef884f3` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated language preferences can improve existing report templates after approval. +### Make generated artifact paths explicit in CLI output + +- ID: `skillops-f9e43237aa` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated delivery-path requests can improve CLI and README artifact discoverability. +### Attach tests and evidence refresh to each upgrade + +- ID: `skillops-0a3a2de7c3` +- Action: `add_eval` +- Decision: `proposal_review` +- Score: `74` +- Risk: `medium` +- Policy: Repeated verification and evidence requests should become focused checks. + +## Patterns + +- `language_default`: Default language preference (support `2`, confidence `0.79`) +- `report_ui`: Report UI and visualization preference (support `5`, confidence `0.95`) +- `approval_safety`: Approval and privacy boundary (support `2`, confidence `0.79`) +- `delivery_format`: Delivery format preference (support `2`, confidence `0.79`) +- `evidence_testing`: Evidence and testing preference (support `2`, confidence `0.79`) + +## Proposals + +### Keep reports Chinese-first with optional English + +- ID: `adapt-18c7517f3d` +- Pattern: `language_default` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` +- Verification: + - `python3 tests/verify_skill_overview.py` + +### Improve report layout, visual hierarchy, and chart readability + +- ID: `adapt-fbfe921ba5` +- Pattern: `report_ui` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` + - `tests/verify_skill_overview.py` +- Verification: + - `python3 tests/verify_skill_overview.py` + - `python3 tests/verify_skill_report_charts.py` + +### Keep adaptive iteration approval-gated + +- ID: `adapt-59d219a1fb` +- Pattern: `approval_safety` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `references/user-memory-policy.md` + - `references/autonomous-adaptation.md` + - `schemas/adaptation-proposal.schema.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + +### Make generated artifact paths explicit in CLI output + +- ID: `adapt-457baca160` +- Pattern: `delivery_format` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/yao.py` + - `README.md` +- Verification: + - `python3 tests/verify_yao_cli.py` + +### Attach tests and evidence refresh to each upgrade + +- ID: `adapt-abfee25d3a` +- Pattern: `evidence_testing` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `tests/verify_adaptation_safety.py` + - `scripts/render_skill_os2_coverage.py` + - `reports/skill_os2_coverage.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + - `python3 tests/verify_skill_os2_coverage.py` + +## Evidence + +- patterns: `reports/user_patterns.json` +- proposals: `reports/adaptation_proposals.json` +- approval_ledger: `reports/adaptation_approval_ledger.json` +- regression: `reports/adaptation_regression_report.json` +- skill_os2_coverage: `reports/skill_os2_coverage.json` +- world_class_ledger: `reports/world_class_evidence_ledger.json` +- evidence_consistency: `reports/evidence_consistency.json` +- benchmark_reproducibility: `reports/benchmark_reproducibility.json` diff --git a/reports/skillops/daily/2026-06-21.json b/reports/skillops/daily/2026-06-21.json new file mode 100644 index 0000000..32f40f8 --- /dev/null +++ b/reports/skillops/daily/2026-06-21.json @@ -0,0 +1,513 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-21", + "skill_dir": ".", + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": true, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": true, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "operations_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "explicit_source_required_for_scan": true, + "implicit_private_log_scan": false, + "raw_content_stored": false, + "redacted_excerpts_only": true, + "proposal_only": true, + "approval_required_for_writes": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "daily_report_counts_as_world_class_evidence": false + }, + "report_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "source_supplied", + "pattern_count", + "proposal_count", + "approval_count", + "pending_review_count", + "applied_count", + "rollback_count", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_pending_count", + "release_lock_ready", + "evidence_consistency_ok", + "writes_source_files", + "auto_patch_enabled", + "failure_count" + ], + "source_of_truth": [ + "summary", + "operations_contract" + ] + }, + "source": { + "path": "evals/adaptation/user_signals.example.jsonl", + "fingerprint_sha256": "48f94a4f3b82f90db29921c13d09c765ec10aabfde1adce65710c137cc6c5abf", + "record_count": 8, + "explicit_source": true, + "raw_content_stored": false, + "redacted_excerpts_only": true + }, + "patterns": [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated report-language signals.", + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "support_count": 5, + "confidence": 0.95, + "reason": "5 redacted records matched repeated artifact-design signals.", + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated governance signals.", + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + "evidence": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated artifact-format signals.", + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + "evidence": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated quality-gate signals.", + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + "evidence": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "opportunity_summary": { + "opportunity_count": 5, + "top_score": 88, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "action_type_counts": { + "patch_existing_skill": 3, + "agents_update": 1, + "add_eval": 1 + }, + "decision_counts": { + "ready_for_approval_review": 1, + "proposal_review": 4 + } + }, + "opportunities": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "decision_policy": { + "schema_version": "1.0", + "contract": "skillops-opportunity-scoring", + "score_range": [ + 0, + 100 + ], + "score_bands": { + "85-100": "ready_for_approval_review", + "70-84": "proposal_review", + "50-69": "observe_more_evidence", + "0-49": "report_only_or_no_action" + }, + "action_types": [ + "add_eval", + "agents_update", + "patch_existing_skill", + "report_only" + ], + "writes_source_files": false, + "auto_patch_enabled": false, + "approval_required_for_writes": true + }, + "approval": { + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "release_state": { + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": true, + "evidence_consistency_ok": true + }, + "actions": [ + { + "key": "review-adaptation-proposals", + "priority": "high", + "action": "Review proposal-only adaptation items before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for the pending world-class ledger entries." + } + ], + "failures": [], + "source_reports": { + "patterns": "reports/user_patterns.json", + "proposals": "reports/adaptation_proposals.json", + "approval_ledger": "reports/adaptation_approval_ledger.json", + "regression": "reports/adaptation_regression_report.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "evidence_consistency": "reports/evidence_consistency.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json" + }, + "artifacts": { + "json": "reports/skillops/daily/2026-06-21.json", + "markdown": "reports/skillops/daily/2026-06-21.md" + } +} diff --git a/reports/skillops/daily/2026-06-21.md b/reports/skillops/daily/2026-06-21.md new file mode 100644 index 0000000..53bc3b2 --- /dev/null +++ b/reports/skillops/daily/2026-06-21.md @@ -0,0 +1,174 @@ +# Daily SkillOps Report + +Generated at: `2026-06-21` + +## Summary + +- decision: `proposal-review` +- source supplied: `false` +- patterns: `5` +- proposals: `5` +- pending approvals: `0` +- applied patches: `0` +- rollbacks: `0` +- local blueprint ready: `true` +- public world-class ready: `false` +- world-class pending: `4` +- release lock ready: `true` +- evidence consistency ok: `true` + +This report is an operations cockpit for explicit-source SkillOps. It does not scan private logs, write source files, apply patches, or count as world-class external or human evidence. + +## Privacy Boundary + +- explicit_source_required_for_scan: `true` +- implicit_private_log_scan: `false` +- raw_content_stored: `false` +- redacted_excerpts_only: `true` +- proposal_only: `true` +- approval_required_for_writes: `true` +- writes_source_files: `false` +- auto_patch_enabled: `false` +- daily_report_counts_as_world_class_evidence: `false` + +## Actions + +- `high` Review proposal-only adaptation items before preparing any approval ledger entry. +- `high` Collect accepted external or human evidence for the pending world-class ledger entries. + +## Opportunities + +- count: `5` +- top score: `88` +- ready for approval review: `1` +### Improve report layout, visual hierarchy, and chart readability + +- ID: `skillops-4ad91c18dd` +- Action: `patch_existing_skill` +- Decision: `ready_for_approval_review` +- Score: `88` +- Risk: `medium` +- Policy: Repeated report layout feedback maps to existing renderer and design doctrine changes. +### Keep adaptive iteration approval-gated + +- ID: `skillops-531e13c1c6` +- Action: `agents_update` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated privacy and approval signals should tighten durable operating guidance. +### Keep reports Chinese-first with optional English + +- ID: `skillops-709ef884f3` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated language preferences can improve existing report templates after approval. +### Make generated artifact paths explicit in CLI output + +- ID: `skillops-f9e43237aa` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated delivery-path requests can improve CLI and README artifact discoverability. +### Attach tests and evidence refresh to each upgrade + +- ID: `skillops-0a3a2de7c3` +- Action: `add_eval` +- Decision: `proposal_review` +- Score: `74` +- Risk: `medium` +- Policy: Repeated verification and evidence requests should become focused checks. + +## Patterns + +- `language_default`: Default language preference (support `2`, confidence `0.79`) +- `report_ui`: Report UI and visualization preference (support `5`, confidence `0.95`) +- `approval_safety`: Approval and privacy boundary (support `2`, confidence `0.79`) +- `delivery_format`: Delivery format preference (support `2`, confidence `0.79`) +- `evidence_testing`: Evidence and testing preference (support `2`, confidence `0.79`) + +## Proposals + +### Keep reports Chinese-first with optional English + +- ID: `adapt-18c7517f3d` +- Pattern: `language_default` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` +- Verification: + - `python3 tests/verify_skill_overview.py` + +### Improve report layout, visual hierarchy, and chart readability + +- ID: `adapt-fbfe921ba5` +- Pattern: `report_ui` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` + - `tests/verify_skill_overview.py` +- Verification: + - `python3 tests/verify_skill_overview.py` + - `python3 tests/verify_skill_report_charts.py` + +### Keep adaptive iteration approval-gated + +- ID: `adapt-59d219a1fb` +- Pattern: `approval_safety` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `references/user-memory-policy.md` + - `references/autonomous-adaptation.md` + - `schemas/adaptation-proposal.schema.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + +### Make generated artifact paths explicit in CLI output + +- ID: `adapt-457baca160` +- Pattern: `delivery_format` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/yao.py` + - `README.md` +- Verification: + - `python3 tests/verify_yao_cli.py` + +### Attach tests and evidence refresh to each upgrade + +- ID: `adapt-abfee25d3a` +- Pattern: `evidence_testing` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `tests/verify_adaptation_safety.py` + - `scripts/render_skill_os2_coverage.py` + - `reports/skill_os2_coverage.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + - `python3 tests/verify_skill_os2_coverage.py` + +## Evidence + +- patterns: `reports/user_patterns.json` +- proposals: `reports/adaptation_proposals.json` +- approval_ledger: `reports/adaptation_approval_ledger.json` +- regression: `reports/adaptation_regression_report.json` +- skill_os2_coverage: `reports/skill_os2_coverage.json` +- world_class_ledger: `reports/world_class_evidence_ledger.json` +- evidence_consistency: `reports/evidence_consistency.json` +- benchmark_reproducibility: `reports/benchmark_reproducibility.json` diff --git a/reports/skillops/daily/2026-07-02.json b/reports/skillops/daily/2026-07-02.json new file mode 100644 index 0000000..0bf6cd8 --- /dev/null +++ b/reports/skillops/daily/2026-07-02.json @@ -0,0 +1,513 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02T06:33:07Z", + "skill_dir": ".", + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "proposal-review", + "source_supplied": false, + "pattern_count": 5, + "proposal_count": 5, + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "operations_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "explicit_source_required_for_scan": true, + "implicit_private_log_scan": false, + "raw_content_stored": false, + "redacted_excerpts_only": true, + "proposal_only": true, + "approval_required_for_writes": true, + "writes_source_files": false, + "auto_patch_enabled": false, + "daily_report_counts_as_world_class_evidence": false + }, + "report_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "source_supplied", + "pattern_count", + "proposal_count", + "approval_count", + "pending_review_count", + "applied_count", + "rollback_count", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_pending_count", + "release_lock_ready", + "evidence_consistency_ok", + "writes_source_files", + "auto_patch_enabled", + "failure_count" + ], + "source_of_truth": [ + "summary", + "operations_contract" + ] + }, + "source": { + "path": "evals/adaptation/user_signals.example.jsonl", + "fingerprint_sha256": "48f94a4f3b82f90db29921c13d09c765ec10aabfde1adce65710c137cc6c5abf", + "record_count": 8, + "explicit_source": true, + "raw_content_stored": false, + "redacted_excerpts_only": true + }, + "patterns": [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated report-language signals.", + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "support_count": 5, + "confidence": 0.95, + "reason": "5 redacted records matched repeated artifact-design signals.", + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated governance signals.", + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + "evidence": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated artifact-format signals.", + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + "evidence": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated quality-gate signals.", + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + "evidence": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "proposals": [ + { + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "risk_level": "low", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "risk_level": "medium", + "status": "proposal-only", + "requires_approval": true, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "opportunity_summary": { + "opportunity_count": 5, + "top_score": 88, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "action_type_counts": { + "patch_existing_skill": 3, + "agents_update": 1, + "add_eval": 1 + }, + "decision_counts": { + "ready_for_approval_review": 1, + "proposal_review": 4 + } + }, + "opportunities": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ] + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ] + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ] + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ] + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ] + } + ], + "decision_policy": { + "schema_version": "1.0", + "contract": "skillops-opportunity-scoring", + "score_range": [ + 0, + 100 + ], + "score_bands": { + "85-100": "ready_for_approval_review", + "70-84": "proposal_review", + "50-69": "observe_more_evidence", + "0-49": "report_only_or_no_action" + }, + "action_types": [ + "add_eval", + "agents_update", + "patch_existing_skill", + "report_only" + ], + "writes_source_files": false, + "auto_patch_enabled": false, + "approval_required_for_writes": true + }, + "approval": { + "approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0 + }, + "release_state": { + "local_blueprint_ready": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "release_lock_ready": false, + "evidence_consistency_ok": true + }, + "actions": [ + { + "key": "review-adaptation-proposals", + "priority": "high", + "action": "Review proposal-only adaptation items before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for the pending world-class ledger entries." + } + ], + "failures": [], + "source_reports": { + "patterns": "reports/user_patterns.json", + "proposals": "reports/adaptation_proposals.json", + "approval_ledger": "reports/adaptation_approval_ledger.json", + "regression": "reports/adaptation_regression_report.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "evidence_consistency": "reports/evidence_consistency.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json" + }, + "artifacts": { + "json": "reports/skillops/daily/2026-07-02.json", + "markdown": "reports/skillops/daily/2026-07-02.md" + } +} diff --git a/reports/skillops/daily/2026-07-02.md b/reports/skillops/daily/2026-07-02.md new file mode 100644 index 0000000..ec4ec29 --- /dev/null +++ b/reports/skillops/daily/2026-07-02.md @@ -0,0 +1,174 @@ +# Daily SkillOps Report + +Generated at: `2026-07-02T06:33:07Z` + +## Summary + +- decision: `proposal-review` +- source supplied: `false` +- patterns: `5` +- proposals: `5` +- pending approvals: `0` +- applied patches: `0` +- rollbacks: `0` +- local blueprint ready: `true` +- public world-class ready: `false` +- world-class pending: `4` +- release lock ready: `false` +- evidence consistency ok: `true` + +This report is an operations cockpit for explicit-source SkillOps. It does not scan private logs, write source files, apply patches, or count as world-class external or human evidence. + +## Privacy Boundary + +- explicit_source_required_for_scan: `true` +- implicit_private_log_scan: `false` +- raw_content_stored: `false` +- redacted_excerpts_only: `true` +- proposal_only: `true` +- approval_required_for_writes: `true` +- writes_source_files: `false` +- auto_patch_enabled: `false` +- daily_report_counts_as_world_class_evidence: `false` + +## Actions + +- `high` Review proposal-only adaptation items before preparing any approval ledger entry. +- `high` Collect accepted external or human evidence for the pending world-class ledger entries. + +## Opportunities + +- count: `5` +- top score: `88` +- ready for approval review: `1` +### Improve report layout, visual hierarchy, and chart readability + +- ID: `skillops-4ad91c18dd` +- Action: `patch_existing_skill` +- Decision: `ready_for_approval_review` +- Score: `88` +- Risk: `medium` +- Policy: Repeated report layout feedback maps to existing renderer and design doctrine changes. +### Keep adaptive iteration approval-gated + +- ID: `skillops-531e13c1c6` +- Action: `agents_update` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated privacy and approval signals should tighten durable operating guidance. +### Keep reports Chinese-first with optional English + +- ID: `skillops-709ef884f3` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated language preferences can improve existing report templates after approval. +### Make generated artifact paths explicit in CLI output + +- ID: `skillops-f9e43237aa` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Policy: Repeated delivery-path requests can improve CLI and README artifact discoverability. +### Attach tests and evidence refresh to each upgrade + +- ID: `skillops-0a3a2de7c3` +- Action: `add_eval` +- Decision: `proposal_review` +- Score: `74` +- Risk: `medium` +- Policy: Repeated verification and evidence requests should become focused checks. + +## Patterns + +- `language_default`: Default language preference (support `2`, confidence `0.79`) +- `report_ui`: Report UI and visualization preference (support `5`, confidence `0.95`) +- `approval_safety`: Approval and privacy boundary (support `2`, confidence `0.79`) +- `delivery_format`: Delivery format preference (support `2`, confidence `0.79`) +- `evidence_testing`: Evidence and testing preference (support `2`, confidence `0.79`) + +## Proposals + +### Keep reports Chinese-first with optional English + +- ID: `adapt-18c7517f3d` +- Pattern: `language_default` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` +- Verification: + - `python3 tests/verify_skill_overview.py` + +### Improve report layout, visual hierarchy, and chart readability + +- ID: `adapt-fbfe921ba5` +- Pattern: `report_ui` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `scripts/render_skill_overview.py` + - `references/artifact-design-doctrine.md` + - `tests/verify_skill_overview.py` +- Verification: + - `python3 tests/verify_skill_overview.py` + - `python3 tests/verify_skill_report_charts.py` + +### Keep adaptive iteration approval-gated + +- ID: `adapt-59d219a1fb` +- Pattern: `approval_safety` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `references/user-memory-policy.md` + - `references/autonomous-adaptation.md` + - `schemas/adaptation-proposal.schema.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + +### Make generated artifact paths explicit in CLI output + +- ID: `adapt-457baca160` +- Pattern: `delivery_format` +- Status: `proposal-only` +- Risk: `low` +- Requires approval: `true` +- Target files: + - `scripts/yao.py` + - `README.md` +- Verification: + - `python3 tests/verify_yao_cli.py` + +### Attach tests and evidence refresh to each upgrade + +- ID: `adapt-abfee25d3a` +- Pattern: `evidence_testing` +- Status: `proposal-only` +- Risk: `medium` +- Requires approval: `true` +- Target files: + - `tests/verify_adaptation_safety.py` + - `scripts/render_skill_os2_coverage.py` + - `reports/skill_os2_coverage.json` +- Verification: + - `python3 tests/verify_adaptation_safety.py` + - `python3 tests/verify_skill_os2_coverage.py` + +## Evidence + +- patterns: `reports/user_patterns.json` +- proposals: `reports/adaptation_proposals.json` +- approval_ledger: `reports/adaptation_approval_ledger.json` +- regression: `reports/adaptation_regression_report.json` +- skill_os2_coverage: `reports/skill_os2_coverage.json` +- world_class_ledger: `reports/world_class_evidence_ledger.json` +- evidence_consistency: `reports/evidence_consistency.json` +- benchmark_reproducibility: `reports/benchmark_reproducibility.json` diff --git a/reports/skillops/weekly/2026-W25.json b/reports/skillops/weekly/2026-W25.json new file mode 100644 index 0000000..5657681 --- /dev/null +++ b/reports/skillops/weekly/2026-W25.json @@ -0,0 +1,322 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-06-21", + "skill_dir": ".", + "decision": "curator-review", + "week_id": "2026-W25", + "daily_report_count": 4, + "opportunity_count": 20, + "unique_opportunity_count": 5, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "top_score": 88, + "skill_count": 11, + "actionable_portfolio_issue_count": 0, + "release_lock_ready": true, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "curator-review", + "week_id": "2026-W25", + "daily_report_count": 4, + "opportunity_count": 20, + "unique_opportunity_count": 5, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "top_score": 88, + "skill_count": 11, + "actionable_portfolio_issue_count": 0, + "release_lock_ready": true, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "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_contract": { + "schema_version": "1.0", + "contract": "weekly-skillops-curator-report", + "top_level_mirrors_summary": true, + "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" + ] + }, + "daily_reports": [ + "reports/skillops/daily/2026-06-16.json", + "reports/skillops/daily/2026-06-17.json", + "reports/skillops/daily/2026-06-20.json", + "reports/skillops/daily/2026-06-21.json" + ], + "opportunity_summary": { + "action_type_counts": { + "add_eval": 1, + "agents_update": 1, + "patch_existing_skill": 3 + }, + "decision_counts": { + "proposal_review": 4, + "ready_for_approval_review": 1 + }, + "risk_level_counts": { + "low": 3, + "medium": 2 + } + }, + "curator_queue": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ], + "daily_report_date": "2026-06-16" + } + ], + "portfolio": { + "skill_count": 11, + "actionable_issue_count": 0, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "no_route_opportunity_count": 0 + }, + "release_state": { + "release_lock_ready": true, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4 + }, + "actions": [ + { + "key": "review-ready-opportunities", + "priority": "high", + "action": "Review the top ready-for-approval SkillOps opportunities before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for pending world-class ledger entries before public claims." + } + ], + "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": "reports/skillops/weekly/2026-W25.json", + "markdown": "reports/skillops/weekly/2026-W25.md" + } +} diff --git a/reports/skillops/weekly/2026-W25.md b/reports/skillops/weekly/2026-W25.md new file mode 100644 index 0000000..7b5feab --- /dev/null +++ b/reports/skillops/weekly/2026-W25.md @@ -0,0 +1,110 @@ +# Weekly SkillOps Curator Report + +Generated at: `2026-06-21` +Week: `2026-W25` + +## Summary + +- decision: `curator-review` +- daily_report_count: `4` +- unique_opportunity_count: `5` +- ready_for_approval_review_count: `1` +- proposal_review_count: `4` +- top_score: `88` +- skill_count: `11` +- actionable_portfolio_issue_count: `0` +- release_lock_ready: `True` +- evidence_consistency_ok: `True` +- public_world_class_ready: `False` +- world_class_pending_count: `4` + +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 + +- 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` + +## Actions + +- `high` Review the top ready-for-approval SkillOps opportunities before preparing any approval ledger entry. +- `high` Collect accepted external or human evidence for pending world-class ledger entries before public claims. + +## Curator Queue + +### Improve report layout, visual hierarchy, and chart readability + +- ID: `skillops-4ad91c18dd` +- Action: `patch_existing_skill` +- Decision: `ready_for_approval_review` +- Score: `88` +- Risk: `medium` +- Source day: `2026-06-16` +- Policy: Repeated report layout feedback maps to existing renderer and design doctrine changes. + +### Keep adaptive iteration approval-gated + +- ID: `skillops-531e13c1c6` +- Action: `agents_update` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Source day: `2026-06-16` +- Policy: Repeated privacy and approval signals should tighten durable operating guidance. + +### Keep reports Chinese-first with optional English + +- ID: `skillops-709ef884f3` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Source day: `2026-06-16` +- Policy: Repeated language preferences can improve existing report templates after approval. + +### Make generated artifact paths explicit in CLI output + +- ID: `skillops-f9e43237aa` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Source day: `2026-06-16` +- Policy: Repeated delivery-path requests can improve CLI and README artifact discoverability. + +### Attach tests and evidence refresh to each upgrade + +- ID: `skillops-0a3a2de7c3` +- Action: `add_eval` +- Decision: `proposal_review` +- Score: `74` +- Risk: `medium` +- Source day: `2026-06-16` +- Policy: Repeated verification and evidence requests should become focused checks. + +## Portfolio Signals + +- skill_count: `11` +- actionable_issue_count: `0` +- route_collision_count: `5` +- actionable_route_collision_count: `0` +- owner_gap_count: `8` +- actionable_owner_gap_count: `0` +- stale_count: `9` +- actionable_stale_count: `0` +- drift_signal_count: `0` +- actionable_drift_signal_count: `0` +- no_route_opportunity_count: `0` + +## Evidence + +- 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` diff --git a/reports/skillops/weekly/2026-W27.json b/reports/skillops/weekly/2026-W27.json new file mode 100644 index 0000000..123e670 --- /dev/null +++ b/reports/skillops/weekly/2026-W27.json @@ -0,0 +1,323 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02T06:33:07Z", + "skill_dir": ".", + "decision": "curator-review", + "week_id": "2026-W27", + "daily_report_count": 5, + "opportunity_count": 25, + "unique_opportunity_count": 5, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "top_score": 88, + "skill_count": 11, + "actionable_portfolio_issue_count": 0, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0, + "summary": { + "decision": "curator-review", + "week_id": "2026-W27", + "daily_report_count": 5, + "opportunity_count": 25, + "unique_opportunity_count": 5, + "ready_for_approval_review_count": 1, + "proposal_review_count": 4, + "observe_more_evidence_count": 0, + "report_only_count": 0, + "top_score": 88, + "skill_count": 11, + "actionable_portfolio_issue_count": 0, + "release_lock_ready": false, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4, + "writes_source_files": false, + "auto_patch_enabled": false, + "failure_count": 0 + }, + "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_contract": { + "schema_version": "1.0", + "contract": "weekly-skillops-curator-report", + "top_level_mirrors_summary": true, + "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" + ] + }, + "daily_reports": [ + "reports/skillops/daily/2026-06-16.json", + "reports/skillops/daily/2026-06-17.json", + "reports/skillops/daily/2026-06-20.json", + "reports/skillops/daily/2026-06-21.json", + "reports/skillops/daily/2026-07-02.json" + ], + "opportunity_summary": { + "action_type_counts": { + "add_eval": 1, + "agents_update": 1, + "patch_existing_skill": 3 + }, + "decision_counts": { + "proposal_review": 4, + "ready_for_approval_review": 1 + }, + "risk_level_counts": { + "low": 3, + "medium": 2 + } + }, + "curator_queue": [ + { + "opportunity_id": "skillops-4ad91c18dd", + "proposal_id": "adapt-fbfe921ba5", + "pattern_id": "report_ui", + "title": "Improve report layout, visual hierarchy, and chart readability", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "medium", + "score": 88, + "score_reasons": [ + "support_count=5", + "confidence=0.95", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 5, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md", + "tests/verify_skill_overview.py" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py", + "python3 tests/verify_skill_report_charts.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-531e13c1c6", + "proposal_id": "adapt-59d219a1fb", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "references/user-memory-policy.md", + "references/autonomous-adaptation.md", + "schemas/adaptation-proposal.schema.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-709ef884f3", + "proposal_id": "adapt-18c7517f3d", + "pattern_id": "language_default", + "title": "Keep reports Chinese-first with optional English", + "opportunity_type": "report-experience", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated language preferences can improve existing report templates after approval.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/render_skill_overview.py", + "references/artifact-design-doctrine.md" + ], + "verification_commands": [ + "python3 tests/verify_skill_overview.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-f9e43237aa", + "proposal_id": "adapt-457baca160", + "pattern_id": "delivery_format", + "title": "Make generated artifact paths explicit in CLI output", + "opportunity_type": "artifact-discoverability", + "action_type": "patch_existing_skill", + "decision": "proposal_review", + "priority": "medium", + "score": 82, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=low", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + "risk_level": "low", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "scripts/yao.py", + "README.md" + ], + "verification_commands": [ + "python3 tests/verify_yao_cli.py" + ], + "daily_report_date": "2026-06-16" + }, + { + "opportunity_id": "skillops-0a3a2de7c3", + "proposal_id": "adapt-abfee25d3a", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 74, + "score_reasons": [ + "support_count=2", + "confidence=0.79", + "risk=medium", + "target_files_present", + "verification_present", + "approval_required" + ], + "policy_reason": "Repeated verification and evidence requests should become focused checks.", + "risk_level": "medium", + "requires_approval": true, + "write_allowed_without_approval": false, + "evidence_count": 2, + "target_files": [ + "tests/verify_adaptation_safety.py", + "scripts/render_skill_os2_coverage.py", + "reports/skill_os2_coverage.json" + ], + "verification_commands": [ + "python3 tests/verify_adaptation_safety.py", + "python3 tests/verify_skill_os2_coverage.py" + ], + "daily_report_date": "2026-06-16" + } + ], + "portfolio": { + "skill_count": 11, + "actionable_issue_count": 0, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "no_route_opportunity_count": 0 + }, + "release_state": { + "release_lock_ready": false, + "evidence_consistency_ok": true, + "public_world_class_ready": false, + "world_class_pending_count": 4 + }, + "actions": [ + { + "key": "review-ready-opportunities", + "priority": "high", + "action": "Review the top ready-for-approval SkillOps opportunities before preparing any approval ledger entry." + }, + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for pending world-class ledger entries before public claims." + } + ], + "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": "reports/skillops/weekly/2026-W27.json", + "markdown": "reports/skillops/weekly/2026-W27.md" + } +} diff --git a/reports/skillops/weekly/2026-W27.md b/reports/skillops/weekly/2026-W27.md new file mode 100644 index 0000000..ea7ec3b --- /dev/null +++ b/reports/skillops/weekly/2026-W27.md @@ -0,0 +1,110 @@ +# Weekly SkillOps Curator Report + +Generated at: `2026-07-02T06:33:07Z` +Week: `2026-W27` + +## Summary + +- decision: `curator-review` +- daily_report_count: `5` +- unique_opportunity_count: `5` +- ready_for_approval_review_count: `1` +- proposal_review_count: `4` +- top_score: `88` +- skill_count: `11` +- actionable_portfolio_issue_count: `0` +- release_lock_ready: `False` +- evidence_consistency_ok: `True` +- public_world_class_ready: `False` +- world_class_pending_count: `4` + +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 + +- 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` + +## Actions + +- `high` Review the top ready-for-approval SkillOps opportunities before preparing any approval ledger entry. +- `high` Collect accepted external or human evidence for pending world-class ledger entries before public claims. + +## Curator Queue + +### Improve report layout, visual hierarchy, and chart readability + +- ID: `skillops-4ad91c18dd` +- Action: `patch_existing_skill` +- Decision: `ready_for_approval_review` +- Score: `88` +- Risk: `medium` +- Source day: `2026-06-16` +- Policy: Repeated report layout feedback maps to existing renderer and design doctrine changes. + +### Keep adaptive iteration approval-gated + +- ID: `skillops-531e13c1c6` +- Action: `agents_update` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Source day: `2026-06-16` +- Policy: Repeated privacy and approval signals should tighten durable operating guidance. + +### Keep reports Chinese-first with optional English + +- ID: `skillops-709ef884f3` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Source day: `2026-06-16` +- Policy: Repeated language preferences can improve existing report templates after approval. + +### Make generated artifact paths explicit in CLI output + +- ID: `skillops-f9e43237aa` +- Action: `patch_existing_skill` +- Decision: `proposal_review` +- Score: `82` +- Risk: `low` +- Source day: `2026-06-16` +- Policy: Repeated delivery-path requests can improve CLI and README artifact discoverability. + +### Attach tests and evidence refresh to each upgrade + +- ID: `skillops-0a3a2de7c3` +- Action: `add_eval` +- Decision: `proposal_review` +- Score: `74` +- Risk: `medium` +- Source day: `2026-06-16` +- Policy: Repeated verification and evidence requests should become focused checks. + +## Portfolio Signals + +- skill_count: `11` +- actionable_issue_count: `0` +- route_collision_count: `5` +- actionable_route_collision_count: `0` +- owner_gap_count: `8` +- actionable_owner_gap_count: `0` +- stale_count: `9` +- actionable_stale_count: `0` +- drift_signal_count: `0` +- actionable_drift_signal_count: `0` +- no_route_opportunity_count: `0` + +## Evidence + +- 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` diff --git a/reports/system-model.json b/reports/system-model.json new file mode 100644 index 0000000..a34ae0c --- /dev/null +++ b/reports/system-model.json @@ -0,0 +1,181 @@ +{ + "skill_name": "yao-meta-skill", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "systems_doctrine": "Structure drives behavior: improve the boundary, feedback loops, drift watch, and leverage points before adding weight.", + "stability": { + "score": 100, + "band": "system-ready" + }, + "boundary_map": { + "owned_job": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "input_boundary": [ + "rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions", + "an existing skill directory that needs refactor, evaluation, packaging, or governance hardening", + "target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution", + "benchmark references, local constraints, desired maturity tier, and review standards" + ], + "output_boundary": "A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations.", + "non_goals": [ + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill", + "raw private material that was not intentionally supplied as skill evidence", + "platform-specific plugin creation when the request is not about an agent skill package" + ], + "constraints": [ + "keep SKILL.md lean and route primarily through frontmatter description", + "put durable guidance in references, executable logic in scripts, and evidence in reports", + "default to the lightest reliable mode before adding governance weight", + "preserve portability across OpenAI, Claude, generic, and Agent Skills compatible targets", + "avoid raw prompt, output, transcript, or private content in telemetry" + ], + "standards": [ + "trigger boundaries must be tested with should-trigger and should-not-trigger cases", + "production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence", + "governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation", + "generated reports should be bilingual or reviewer-friendly when they are user-facing", + "each new asset must earn its place by reducing ambiguity, risk, or repeated work" + ], + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "maturity_assumption": "governed" + }, + "feedback_loops": [ + { + "name": "Intent boundary loop", + "signal": "Intent confidence score is 100/100.", + "response": "Ask only the highest-leverage clarification before adding package weight.", + "evidence": "reports/intent-confidence.md and reports/intent-dialogue.md" + }, + { + "name": "Reference synthesis loop", + "signal": "Benchmark patterns are useful only after they are abstracted into borrow and avoid guidance.", + "response": "Borrow one pattern at a time and keep the rest as reviewer-visible evidence.", + "evidence": "reports/reference-synthesis.md", + "current_patterns": [ + "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package.", + "Borrow the way it turns a messy workflow into a repeatable operating path.", + "Borrow the clear execution entrypoints and command structure." + ] + }, + { + "name": "Output quality loop", + "signal": "Generated output may fail in recurring domain-specific ways.", + "response": "Apply predicted output-risk families as self-repair checks before final output.", + "evidence": "reports/output-risk-profile.md", + "current_risk_families": [ + "Markdown readability", + "Citation and footnote clutter", + "Screenshot and visual capture", + "Code and command safety", + "Tone and specificity" + ] + }, + { + "name": "Reviewer feedback loop", + "signal": "Human review catches drift that static checks miss.", + "response": "Capture lightweight feedback and turn repeated findings into gates or references.", + "evidence": "reports/review-viewer.html and feedback records" + }, + { + "name": "Lifecycle loop", + "signal": "As reuse grows, the skill needs stronger gates, ownership, and regression evidence.", + "response": "Promote only when the next gate improves reliability more than context cost.", + "evidence": "manifest.json, reports/iteration-directions.md, and governance checks" + } + ], + "drift_watch": [ + { + "name": "Trigger drift", + "watch_signal": "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "countermeasure": "Add near-neighbor exclusions and route evals before expanding workflow steps.", + "cadence": "per trigger or description change" + }, + { + "name": "Output drift", + "watch_signal": "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "countermeasure": "Refresh output-risk and artifact-design profiles, then add one self-repair check.", + "cadence": "after the first 3-5 real uses", + "risk_families": [ + "Markdown readability", + "Citation and footnote clutter", + "Screenshot and visual capture", + "Code and command safety", + "Tone and specificity" + ] + }, + { + "name": "Reference drift", + "watch_signal": "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "countermeasure": "Re-run reference synthesis and keep only patterns that improve the current boundary.", + "cadence": "per material benchmark or product assumption change" + }, + { + "name": "Governance drift", + "watch_signal": "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal.", + "countermeasure": "Promote maturity tier and add reviewer-visible lifecycle evidence.", + "cadence": "monthly" + } + ], + "failure_pattern_map": [ + { + "family": "Boundary failure", + "symptom": "The skill handles nearby requests that were never part of the recurring job.", + "repair": "Narrow the description and add explicit non-goals before adding more execution steps." + }, + { + "family": "Feedback gap", + "symptom": "The skill has rules but no signal telling authors which rule should change after use.", + "repair": "Turn repeated reviewer feedback into one eval, one reference note, or one self-repair check." + }, + { + "family": "Output degradation", + "symptom": "The result is structurally correct but generic, cluttered, or weakly matched to the user's domain.", + "repair": "Use output-risk families as pre-final checks.", + "current_risk_families": [ + "Markdown readability", + "Citation and footnote clutter", + "Screenshot and visual capture", + "Code and command safety", + "Tone and specificity" + ] + }, + { + "family": "Prompt-behavior mismatch", + "symptom": "The role, task, and format are copied from a prompt instead of becoming stable skill behavior.", + "repair": "Convert reusable role/task/format assumptions into workflow, reports, or references.", + "watch_axes": [] + } + ], + "leverage_points": [ + { + "rank": 2, + "point": "Tune the frontmatter description", + "why": "The description is the highest-leverage routing surface.", + "move": "Name the recurring job, expected input, output, and strongest non-goal in compact language." + }, + { + "rank": 3, + "point": "Install output self-repair checks", + "why": "The likely failure families are: Markdown readability, Citation and footnote clutter, Screenshot and visual capture.", + "move": "Add only the checks that prevent recurring output mistakes." + }, + { + "rank": 4, + "point": "Borrow one pattern, not a whole product", + "why": "External references improve quality when reduced to structure, not copied as surface style.", + "move": "Start from: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts." + }, + { + "rank": 5, + "point": "Close the lifecycle loop", + "why": "Team-reused skills need visible ownership, review cadence, and regression evidence.", + "move": "Keep manifest, review viewer, and iteration directions aligned after each material change." + } + ], + "reviewer_rule": "Reviewer should ask whether the skill's structure will keep producing the desired behavior after repeated real use." +} \ No newline at end of file diff --git a/reports/system-model.md b/reports/system-model.md new file mode 100644 index 0000000..17f31f8 --- /dev/null +++ b/reports/system-model.md @@ -0,0 +1,171 @@ +# System Model + +Skill: `yao-meta-skill` + +- Stability score: `100/100` +- Stability band: `system-ready` +- Doctrine: Structure drives behavior: improve the boundary, feedback loops, drift watch, and leverage points before adding weight. + +## System Boundary Map + +- Owned job: Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse. +- Output boundary: A working skill package with lean SKILL.md, aligned agents/interface.yaml, justified references, scripts only when useful, eval evidence, reports, packaging metadata, and clear next iteration recommendations. +- Maturity assumption: `governed` +- Input boundary: + - rough workflow notes, SOPs, runbooks, prompts, transcripts, documents, or repeated task descriptions + - an existing skill directory that needs refactor, evaluation, packaging, or governance hardening + - target platform requirements such as OpenAI, Claude, generic Agent Skills, or team distribution + - benchmark references, local constraints, desired maturity tier, and review standards +- Non-goals: + - one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill + - general code review or debugging unless the user is packaging that workflow as a skill + - raw private material that was not intentionally supplied as skill evidence + - platform-specific plugin creation when the request is not about an agent skill package +- Constraints: + - keep SKILL.md lean and route primarily through frontmatter description + - put durable guidance in references, executable logic in scripts, and evidence in reports + - default to the lightest reliable mode before adding governance weight + - preserve portability across OpenAI, Claude, generic, and Agent Skills compatible targets + - avoid raw prompt, output, transcript, or private content in telemetry +- Standards: + - trigger boundaries must be tested with should-trigger and should-not-trigger cases + - production and higher maturity work needs output eval, trust, runtime conformance, and Review Studio evidence + - governed work needs owner, review cadence, permission approvals, registry metadata, package verification, and install simulation + - generated reports should be bilingual or reviewer-friendly when they are user-facing + - each new asset must earn its place by reducing ambiguity, risk, or repeated work +- Human judgment boundary: + - Ask one focused clarification when the real job, output, or exclusion boundary is unclear. + - Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints. + - Do not silently broaden the skill into adjacent jobs just because the examples are nearby. + +## Feedback Loops + +### Intent boundary loop + +- Signal: Intent confidence score is 100/100. +- Response: Ask only the highest-leverage clarification before adding package weight. +- Evidence: reports/intent-confidence.md and reports/intent-dialogue.md + +### Reference synthesis loop + +- Signal: Benchmark patterns are useful only after they are abstracted into borrow and avoid guidance. +- Response: Borrow one pattern at a time and keep the rest as reviewer-visible evidence. +- Evidence: reports/reference-synthesis.md +- Current patterns: + - Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. + - Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. + - Borrow the discipline of defining what the skill should not own before growing the package. + - Borrow the way it turns a messy workflow into a repeatable operating path. + - Borrow the clear execution entrypoints and command structure. + +### Output quality loop + +- Signal: Generated output may fail in recurring domain-specific ways. +- Response: Apply predicted output-risk families as self-repair checks before final output. +- Evidence: reports/output-risk-profile.md +- Current risk families: + - Markdown readability + - Citation and footnote clutter + - Screenshot and visual capture + - Code and command safety + - Tone and specificity + +### Reviewer feedback loop + +- Signal: Human review catches drift that static checks miss. +- Response: Capture lightweight feedback and turn repeated findings into gates or references. +- Evidence: reports/review-viewer.html and feedback records + +### Lifecycle loop + +- Signal: As reuse grows, the skill needs stronger gates, ownership, and regression evidence. +- Response: Promote only when the next gate improves reliability more than context cost. +- Evidence: manifest.json, reports/iteration-directions.md, and governance checks + +## Delay And Drift Watch + +### Trigger drift + +- Watch signal: Users start invoking the skill for adjacent one-off or explanation-only requests. +- Countermeasure: Add near-neighbor exclusions and route evals before expanding workflow steps. +- Cadence: per trigger or description change + +### Output drift + +- Watch signal: Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain. +- Countermeasure: Refresh output-risk and artifact-design profiles, then add one self-repair check. +- Cadence: after the first 3-5 real uses +- Risk families: + - Markdown readability + - Citation and footnote clutter + - Screenshot and visual capture + - Code and command safety + - Tone and specificity + +### Reference drift + +- Watch signal: Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff. +- Countermeasure: Re-run reference synthesis and keep only patterns that improve the current boundary. +- Cadence: per material benchmark or product assumption change + +### Governance drift + +- Watch signal: Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal. +- Countermeasure: Promote maturity tier and add reviewer-visible lifecycle evidence. +- Cadence: monthly + +## Failure Pattern Map + +### Boundary failure + +- Symptom: The skill handles nearby requests that were never part of the recurring job. +- Repair: Narrow the description and add explicit non-goals before adding more execution steps. + +### Feedback gap + +- Symptom: The skill has rules but no signal telling authors which rule should change after use. +- Repair: Turn repeated reviewer feedback into one eval, one reference note, or one self-repair check. + +### Output degradation + +- Symptom: The result is structurally correct but generic, cluttered, or weakly matched to the user's domain. +- Repair: Use output-risk families as pre-final checks. +- Current Risk Families: + - Markdown readability + - Citation and footnote clutter + - Screenshot and visual capture + - Code and command safety + - Tone and specificity + +### Prompt-behavior mismatch + +- Symptom: The role, task, and format are copied from a prompt instead of becoming stable skill behavior. +- Repair: Convert reusable role/task/format assumptions into workflow, reports, or references. + +## Highest Leverage Moves + +### 2. Tune the frontmatter description + +- Why: The description is the highest-leverage routing surface. +- Move: Name the recurring job, expected input, output, and strongest non-goal in compact language. + +### 3. Install output self-repair checks + +- Why: The likely failure families are: Markdown readability, Citation and footnote clutter, Screenshot and visual capture. +- Move: Add only the checks that prevent recurring output mistakes. + +### 4. Borrow one pattern, not a whole product + +- Why: External references improve quality when reduced to structure, not copied as surface style. +- Move: Start from: Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts. + +### 5. Close the lifecycle loop + +- Why: Team-reused skills need visible ownership, review cadence, and regression evidence. +- Move: Keep manifest, review viewer, and iteration directions aligned after each material change. + +## Reviewer Use + +- Reviewer should ask whether the skill's structure will keep producing the desired behavior after repeated real use. +- Prefer changing the system boundary, feedback loop, or leverage point before adding more prose. +- If a problem repeats, convert it into a named failure pattern and one regression check. diff --git a/reports/telemetry_hook_recipes.json b/reports/telemetry_hook_recipes.json new file mode 100644 index 0000000..b9a6db7 --- /dev/null +++ b/reports/telemetry_hook_recipes.json @@ -0,0 +1,338 @@ +{ + "ok": true, + "schema_version": "1.0", + "skill_dir": ".", + "summary": { + "recipe_count": 5, + "native_auto_capture_count": 0, + "metadata_only_recipe_count": 5 + }, + "privacy_contract": { + "raw_content_allowed": false, + "blocked_fields": [ + "content", + "input", + "inputs", + "message", + "messages", + "note", + "output", + "outputs", + "prompt", + "raw", + "text", + "transcript" + ], + "allowed_fields": [ + "event", + "skill", + "version", + "source", + "command", + "activation_type", + "outcome", + "failure_type", + "timestamp" + ] + }, + "recipes": [ + { + "id": "browser-extension", + "client": "Browser extension", + "command": "browser-extension", + "event": "skill_activation", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "trigger_points": [ + "Skill is explicitly selected by the user.", + "Skill activation is accepted by the host client." + ], + "source": "external", + "native_auto_capture": false, + "integration_status": "client-hook-recipe", + "metadata_only": true, + "emit_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_activation", + "--activation-type", + "explicit", + "--outcome", + "accepted", + "--failure-type", + "none", + "--command", + "browser-extension" + ], + "emit_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_activation --activation-type explicit --outcome accepted --failure-type none --command browser-extension", + "dry_run_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_activation", + "--activation-type", + "explicit", + "--outcome", + "accepted", + "--failure-type", + "none", + "--command", + "browser-extension", + "--dry-run" + ], + "dry_run_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_activation --activation-type explicit --outcome accepted --failure-type none --command browser-extension --dry-run" + }, + { + "id": "chrome-extension", + "client": "Chrome extension", + "command": "chrome-extension", + "event": "skill_output", + "activation_type": "manual", + "outcome": "edited", + "failure_type": "none", + "trigger_points": [ + "User keeps or edits a skill-generated artifact.", + "Extension records only the final outcome class, not page content." + ], + "source": "external", + "native_auto_capture": false, + "integration_status": "client-hook-recipe", + "metadata_only": true, + "emit_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_output", + "--activation-type", + "manual", + "--outcome", + "edited", + "--failure-type", + "none", + "--command", + "chrome-extension" + ], + "emit_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_output --activation-type manual --outcome edited --failure-type none --command chrome-extension", + "dry_run_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_output", + "--activation-type", + "manual", + "--outcome", + "edited", + "--failure-type", + "none", + "--command", + "chrome-extension", + "--dry-run" + ], + "dry_run_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_output --activation-type manual --outcome edited --failure-type none --command chrome-extension --dry-run" + }, + { + "id": "vscode-extension", + "client": "VS Code extension", + "command": "vscode-extension", + "event": "skill_activation", + "activation_type": "implicit", + "outcome": "accepted", + "failure_type": "none", + "trigger_points": [ + "Workspace skill route chooses this skill.", + "Extension records route outcome without file paths or code snippets." + ], + "source": "external", + "native_auto_capture": false, + "integration_status": "client-hook-recipe", + "metadata_only": true, + "emit_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_activation", + "--activation-type", + "implicit", + "--outcome", + "accepted", + "--failure-type", + "none", + "--command", + "vscode-extension" + ], + "emit_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_activation --activation-type implicit --outcome accepted --failure-type none --command vscode-extension", + "dry_run_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_activation", + "--activation-type", + "implicit", + "--outcome", + "accepted", + "--failure-type", + "none", + "--command", + "vscode-extension", + "--dry-run" + ], + "dry_run_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_activation --activation-type implicit --outcome accepted --failure-type none --command vscode-extension --dry-run" + }, + { + "id": "cli-wrapper", + "client": "CLI wrapper", + "command": "cli-wrapper", + "event": "script_run", + "activation_type": "manual", + "outcome": "unknown", + "failure_type": "none", + "trigger_points": [ + "Wrapper starts or finishes a known skill workflow command.", + "Wrapper records command family only, never command arguments." + ], + "source": "external", + "native_auto_capture": false, + "integration_status": "client-hook-recipe", + "metadata_only": true, + "emit_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "script_run", + "--activation-type", + "manual", + "--outcome", + "unknown", + "--failure-type", + "none", + "--command", + "cli-wrapper" + ], + "emit_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event script_run --activation-type manual --outcome unknown --failure-type none --command cli-wrapper", + "dry_run_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "script_run", + "--activation-type", + "manual", + "--outcome", + "unknown", + "--failure-type", + "none", + "--command", + "cli-wrapper", + "--dry-run" + ], + "dry_run_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event script_run --activation-type manual --outcome unknown --failure-type none --command cli-wrapper --dry-run" + }, + { + "id": "provider-adapter", + "client": "Provider adapter", + "command": "provider-adapter", + "event": "skill_output", + "activation_type": "manual", + "outcome": "accepted", + "failure_type": "none", + "trigger_points": [ + "Provider adapter receives an accepted or rejected skill output signal.", + "Adapter records only normalized outcome metadata." + ], + "source": "external", + "native_auto_capture": false, + "integration_status": "client-hook-recipe", + "metadata_only": true, + "emit_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_output", + "--activation-type", + "manual", + "--outcome", + "accepted", + "--failure-type", + "none", + "--command", + "provider-adapter" + ], + "emit_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_output --activation-type manual --outcome accepted --failure-type none --command provider-adapter", + "dry_run_argv": [ + "python3", + "scripts/yao.py", + "telemetry-emit", + ".", + "--output-jsonl", + ".yao/telemetry_spool/external_events.jsonl", + "--event", + "skill_output", + "--activation-type", + "manual", + "--outcome", + "accepted", + "--failure-type", + "none", + "--command", + "provider-adapter", + "--dry-run" + ], + "dry_run_command": "python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_output --activation-type manual --outcome accepted --failure-type none --command provider-adapter --dry-run" + } + ], + "artifacts": { + "json": "reports/telemetry_hook_recipes.json", + "markdown": "reports/telemetry_hook_recipes.md", + "spool_jsonl": ".yao/telemetry_spool/external_events.jsonl", + "import_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "import_argv": [ + "python3", + "scripts/yao.py", + "telemetry-import", + ".", + "--input-jsonl", + ".yao/telemetry_spool/external_events.jsonl" + ] + }, + "warnings": [ + "These recipes do not prove native host-client integration; they define the local metadata-only hook contract for those integrations." + ], + "failures": [] +} diff --git a/reports/telemetry_hook_recipes.md b/reports/telemetry_hook_recipes.md new file mode 100644 index 0000000..b32e5be --- /dev/null +++ b/reports/telemetry_hook_recipes.md @@ -0,0 +1,35 @@ +# Telemetry Hook Recipes + +These recipes show how Browser, Chrome, IDE, wrapper, or provider-side integrations can call `telemetry-emit` without collecting raw prompts, outputs, transcripts, messages, notes, arguments, or file content. + +They are client hook recipes, not proof that a host client is already natively integrated. + +## Summary + +- Recipe count: `5` +- Native auto-capture integrations claimed: `0` +- Default spool: `.yao/telemetry_spool/external_events.jsonl` + +## Recipes + +| Client | Command | Event | Outcome | Dry run | +| --- | --- | --- | --- | --- | +| Browser extension | `browser-extension` | `skill_activation` | `accepted` | `python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_activation --activation-type explicit --outcome accepted --failure-type none --command browser-extension --dry-run` | +| Chrome extension | `chrome-extension` | `skill_output` | `edited` | `python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_output --activation-type manual --outcome edited --failure-type none --command chrome-extension --dry-run` | +| VS Code extension | `vscode-extension` | `skill_activation` | `accepted` | `python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_activation --activation-type implicit --outcome accepted --failure-type none --command vscode-extension --dry-run` | +| CLI wrapper | `cli-wrapper` | `script_run` | `unknown` | `python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event script_run --activation-type manual --outcome unknown --failure-type none --command cli-wrapper --dry-run` | +| Provider adapter | `provider-adapter` | `skill_output` | `accepted` | `python3 scripts/yao.py telemetry-emit . --output-jsonl .yao/telemetry_spool/external_events.jsonl --event skill_output --activation-type manual --outcome accepted --failure-type none --command provider-adapter --dry-run` | + +## Import + +After a client finishes a batch, import the local spool: + +```bash +python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl +``` + +## Privacy Contract + +- Allowed fields: `event`, `skill`, `version`, `source`, `command`, `activation_type`, `outcome`, `failure_type`, `timestamp`. +- Blocked raw-content fields: `content, input, inputs, message, messages, note, output, outputs, prompt, raw, text, transcript`. +- Client integrations must map local state to normalized outcome classes before calling the hook. diff --git a/reports/upgrade_check.json b/reports/upgrade_check.json new file mode 100644 index 0000000..73b1562 --- /dev/null +++ b/reports/upgrade_check.json @@ -0,0 +1,96 @@ +{ + "ok": true, + "schema_version": "2.0", + "generated_at": "2026-06-13", + "previous": { + "name": "yao-meta-skill", + "version": "1.0.0", + "targets": [ + "openai", + "claude", + "generic" + ] + }, + "current": { + "name": "yao-meta-skill", + "version": "1.1.0", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ] + }, + "summary": { + "declared_bump": "minor", + "recommended_bump": "minor", + "breaking_change_count": 0, + "added_target_count": 2, + "metadata_change_count": 1, + "checksum_change_count": 2, + "failure_count": 0, + "warning_count": 0 + }, + "upgrade_diff": { + "name_changed": false, + "added_targets": [ + "agent-skills-compatible", + "vscode" + ], + "removed_targets": [], + "compatibility_changes": [ + { + "target": "agent-skills", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "agent-skills-compatible", + "from": "missing", + "to": "pass", + "direction": "improved" + }, + { + "target": "vscode", + "from": "missing", + "to": "pass", + "direction": "improved" + } + ], + "metadata_changes": [ + { + "field": "description", + "from": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes.", + "to": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs." + } + ], + "checksum_changes": [ + { + "field": "archive_sha256", + "from": "", + "to": "c56a8519244c0bc8ec17c92d4641c832629cc059f4b51fe177a2c039515253a7" + }, + { + "field": "package_sha256", + "from": "0000000000000000000000000000000000000000000000000000000000000000", + "to": "db71157184dc649ee72dfcaf399204d44a366d942393d055571e0137d42c9f7c" + } + ] + }, + "release_notes": [ + "Recommended version bump: minor.", + "Added targets: agent-skills-compatible, vscode.", + "Package or archive checksum changed; reviewers should verify package artifacts before release.", + "Package verification evidence: reports/package_verification.md." + ], + "failures": [], + "warnings": [], + "artifacts": { + "previous_package": "registry/examples/yao-meta-skill-1.0.0.json", + "current_package": "reports/registry_audit.json", + "json": "tests/tmp_review_studio/upgrade_check.json", + "markdown": "tests/tmp_review_studio/upgrade_check.md" + } +} diff --git a/reports/upgrade_check.md b/reports/upgrade_check.md new file mode 100644 index 0000000..55f3cb2 --- /dev/null +++ b/reports/upgrade_check.md @@ -0,0 +1,31 @@ +# Upgrade Check + +- OK: `True` +- Previous: `yao-meta-skill 1.0.0` +- Current: `yao-meta-skill 1.1.0` +- Declared bump: `minor` +- Recommended bump: `minor` +- Breaking changes: `0` + +## Upgrade Diff + +- Added targets: `agent-skills-compatible, vscode` +- Removed targets: `none` +- Compatibility changes: `3` +- Metadata changes: `1` +- Checksum changes: `2` + +## Release Notes + +- Recommended version bump: minor. +- Added targets: agent-skills-compatible, vscode. +- Package or archive checksum changed; reviewers should verify package artifacts before release. +- Package verification evidence: reports/package_verification.md. + +## Failures + +- None + +## Warnings + +- None diff --git a/reports/user_patterns.json b/reports/user_patterns.json new file mode 100644 index 0000000..d73a4f8 --- /dev/null +++ b/reports/user_patterns.json @@ -0,0 +1,136 @@ +{ + "ok": true, + "schema_version": "1.0", + "generated_at": "2026-06-15T00:00:00Z", + "skill_dir": ".", + "source": { + "label": "user_signals.example.jsonl", + "path": "evals/adaptation/user_signals.example.jsonl", + "fingerprint_sha256": "48f94a4f3b82f90db29921c13d09c765ec10aabfde1adce65710c137cc6c5abf", + "explicit_source": true, + "record_count": 8 + }, + "privacy_contract": { + "local_only": true, + "explicit_source_required": true, + "implicit_private_log_scan": false, + "raw_content_stored": false, + "redacted_excerpts_only": true, + "redacted_excerpt_limit": 240, + "writes_repository_files": false + }, + "summary": { + "record_count": 8, + "pattern_count": 5, + "discarded_signal_count": 0, + "min_support": 2, + "failure_count": 0 + }, + "patterns": [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated report-language signals.", + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + } + ] + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "support_count": 5, + "confidence": 0.95, + "reason": "5 redacted records matched repeated artifact-design signals.", + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + "evidence": [ + { + "record_id": "line-1", + "excerpt": "新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。" + }, + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-3", + "excerpt": "报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。" + } + ] + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated governance signals.", + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + "evidence": [ + { + "record_id": "line-5", + "excerpt": "自适应升级必须先生成提案,不能直接自动修改源文件。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated artifact-format signals.", + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + "evidence": [ + { + "record_id": "line-2", + "excerpt": "HTML 报告需要双语能力,但默认内容应该保持中文简体。" + }, + { + "record_id": "line-6", + "excerpt": "用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。" + } + ] + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "support_count": 2, + "confidence": 0.79, + "reason": "2 redacted records matched repeated quality-gate signals.", + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + "evidence": [ + { + "record_id": "line-7", + "excerpt": "每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。" + }, + { + "record_id": "line-8", + "excerpt": "涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。" + } + ] + } + ], + "discarded_signals": [], + "failures": [], + "artifacts": { + "json": "reports/user_patterns.json", + "markdown": "reports/user_patterns.md" + } +} diff --git a/reports/user_patterns.md b/reports/user_patterns.md new file mode 100644 index 0000000..855d5eb --- /dev/null +++ b/reports/user_patterns.md @@ -0,0 +1,72 @@ +# User Pattern Summary + +- Generated at: `2026-06-15T00:00:00Z` +- Local only: `true` +- Explicit source: `evals/adaptation/user_signals.example.jsonl` +- Records: `8` +- Patterns: `5` +- Discarded signals: `0` + +## Privacy Contract + +- No implicit private log scan. +- No unredacted raw content stored. +- Scan and proposal stages do not write source files. + +## Patterns + +### Default language preference + +- Pattern: `language_default` +- Support: `2` +- Confidence: `0.79` +- Reason: 2 redacted records matched repeated report-language signals. +- Recommended action: Keep generated reports Chinese-first with an English switch where user-facing. +- Redacted evidence: + - `line-1`: 新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。 + - `line-2`: HTML 报告需要双语能力,但默认内容应该保持中文简体。 + +### Report UI and visualization preference + +- Pattern: `report_ui` +- Support: `5` +- Confidence: `0.95` +- Reason: 5 redacted records matched repeated artifact-design signals. +- Recommended action: Prioritize white-background Kami-style reports with readable charts and stable navigation. +- Redacted evidence: + - `line-1`: 新生成的 Skill 报告默认使用中文简体,并在右上角提供英文切换。 + - `line-2`: HTML 报告需要双语能力,但默认内容应该保持中文简体。 + - `line-3`: 报告排版采用白底 Kami 风格,图表、模块和导航都要清晰。 + +### Approval and privacy boundary + +- Pattern: `approval_safety` +- Support: `2` +- Confidence: `0.79` +- Reason: 2 redacted records matched repeated governance signals. +- Recommended action: Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path. +- Redacted evidence: + - `line-5`: 自适应升级必须先生成提案,不能直接自动修改源文件。 + - `line-6`: 用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。 + +### Delivery format preference + +- Pattern: `delivery_format` +- Support: `2` +- Confidence: `0.79` +- Reason: 2 redacted records matched repeated artifact-format signals. +- Recommended action: Surface stable artifact paths and formats in CLI output and generated summaries. +- Redacted evidence: + - `line-2`: HTML 报告需要双语能力,但默认内容应该保持中文简体。 + - `line-6`: 用户偏好扫描必须由用户提供明确路径,不要默认扫描私人日志。 + +### Evidence and testing preference + +- Pattern: `evidence_testing` +- Support: `2` +- Confidence: `0.79` +- Reason: 2 redacted records matched repeated quality-gate signals. +- Recommended action: Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade. +- Redacted evidence: + - `line-7`: 每次升级都需要测试、覆盖报告和可审计证据,推送前要跑 CI。 + - `line-8`: 涉及 GitHub 推送时,要保留证据链,避免把计划当作完成证明。 diff --git a/reports/world_class_claim_guard.json b/reports/world_class_claim_guard.json new file mode 100644 index 0000000..00fb284 --- /dev/null +++ b/reports/world_class_claim_guard.json @@ -0,0 +1,859 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "ledger_ready_to_claim_world_class": false, + "ledger_pending_count": 4, + "claim_surface_count": 201, + "json_claim_surface_count": 97, + "metadata_claim_surface_count": 98, + "package_claim_surface_count": 17, + "violation_count": 0, + "overclaim_guard_active": true, + "decision": "claim-guard-pass-evidence-pending" + }, + "rules": [ + { + "key": "ready-to-claim-true", + "reason": "ready_to_claim_world_class cannot be true while ledger evidence is pending.", + "pattern": "ready[\\s_-]*to[\\s_-]*claim[\\s_-]*world[\\s_-]*class\\s*[:=]\\s*`?true`?" + }, + { + "key": "world-class-ready-true", + "reason": "world-class readiness cannot be claimed before accepted external and human evidence exists.", + "pattern": "(public\\s+)?world[\\s_-]*class[\\s_-]*ready\\s*[:=]\\s*`?true`?" + }, + { + "key": "json-world-class-ready-true", + "reason": "machine-readable claim fields must remain false until the ledger is ready.", + "pattern": "\"(?:ready_to_claim_world_class|world_class_ready|public_world_class_ready)\"\\s*:\\s*true" + }, + { + "key": "completion-phrase", + "reason": "completion language is blocked until the world-class ledger is accepted.", + "pattern": "(public\\s+)?world[\\s_-]*class\\s+(?:complete|completed|achieved|certified)" + }, + { + "key": "zh-completion-phrase", + "reason": "中文完成态表述必须等到 ledger ready 后才能出现。", + "pattern": "世界级(?:已完成|完成|已达成|达成|准备就绪|可声明|认证通过)" + } + ], + "scanned_surfaces": [ + { + "path": "README.md", + "violation_count": 0 + }, + { + "path": "SKILL.md", + "violation_count": 0 + }, + { + "path": "agents/interface.yaml", + "violation_count": 0 + }, + { + "path": "dist/manifest.json", + "violation_count": 0 + }, + { + "path": "dist/targets/claude/README.md", + "violation_count": 0 + }, + { + "path": "dist/targets/claude/adapter.json", + "violation_count": 0 + }, + { + "path": "dist/targets/generic/adapter.json", + "violation_count": 0 + }, + { + "path": "dist/targets/openai/adapter.json", + "violation_count": 0 + }, + { + "path": "dist/targets/vscode/README.md", + "violation_count": 0 + }, + { + "path": "dist/targets/vscode/adapter.json", + "violation_count": 0 + }, + { + "path": "docs/README.fr-FR.md", + "violation_count": 0 + }, + { + "path": "docs/README.ja-JP.md", + "violation_count": 0 + }, + { + "path": "docs/README.ru-RU.md", + "violation_count": 0 + }, + { + "path": "docs/README.zh-CN.md", + "violation_count": 0 + }, + { + "path": "docs/migration-v2.md", + "violation_count": 0 + }, + { + "path": "evidence/world_class/README.md", + "violation_count": 0 + }, + { + "path": "evidence/world_class/intake.schema.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/human-adjudication.intake.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "violation_count": 0 + }, + { + "path": "evidence/world_class/templates/provider-holdout.intake.json", + "violation_count": 0 + }, + { + "path": "manifest.json", + "violation_count": 0 + }, + { + "path": "registry/examples/yao-meta-skill-1.0.0.json", + "violation_count": 0 + }, + { + "path": "registry/index.json", + "violation_count": 0 + }, + { + "path": "registry/index.schema.json", + "violation_count": 0 + }, + { + "path": "registry/package.schema.json", + "violation_count": 0 + }, + { + "path": "registry/packages/yao-meta-skill.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_approval_ledger.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_proposals.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_proposals.md", + "violation_count": 0 + }, + { + "path": "reports/adaptation_regression_report.json", + "violation_count": 0 + }, + { + "path": "reports/adaptation_regression_report.md", + "violation_count": 0 + }, + { + "path": "reports/adoption_drift_report.json", + "violation_count": 0 + }, + { + "path": "reports/adoption_drift_report.md", + "violation_count": 0 + }, + { + "path": "reports/architecture_maintainability.json", + "violation_count": 0 + }, + { + "path": "reports/architecture_maintainability.md", + "violation_count": 0 + }, + { + "path": "reports/artifact-design-profile.json", + "violation_count": 0 + }, + { + "path": "reports/artifact-design-profile.md", + "violation_count": 0 + }, + { + "path": "reports/baseline-compare.json", + "violation_count": 0 + }, + { + "path": "reports/baseline-compare.md", + "violation_count": 0 + }, + { + "path": "reports/benchmark_methodology.md", + "violation_count": 0 + }, + { + "path": "reports/benchmark_reproducibility.json", + "violation_count": 0 + }, + { + "path": "reports/benchmark_reproducibility.md", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/DO_NOT_OPEN_answer_key.json", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/README.md", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/adjudication.md", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/blind-pack.json", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-01-support-triage.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-02-monthly-revenue-reconciliation.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-03-webinar-repurposing.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-04-incident-postmortem.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/case-05-pr-review-followup.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/index.html", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/review-decisions-template.json", + "violation_count": 0 + }, + { + "path": "reports/blind-human-review-2026-06-29/review-decisions.recorded.json", + "violation_count": 0 + }, + { + "path": "reports/candidate_registry.json", + "violation_count": 0 + }, + { + "path": "reports/candidate_registry.md", + "violation_count": 0 + }, + { + "path": "reports/compiled_targets.json", + "violation_count": 0 + }, + { + "path": "reports/compiled_targets.md", + "violation_count": 0 + }, + { + "path": "reports/conformance_matrix.json", + "violation_count": 0 + }, + { + "path": "reports/conformance_matrix.md", + "violation_count": 0 + }, + { + "path": "reports/context_budget.json", + "violation_count": 0 + }, + { + "path": "reports/context_budget.md", + "violation_count": 0 + }, + { + "path": "reports/context_budget_summary.json", + "violation_count": 0 + }, + { + "path": "reports/description_drift_history.md", + "violation_count": 0 + }, + { + "path": "reports/description_optimization.json", + "violation_count": 0 + }, + { + "path": "reports/description_optimization.md", + "violation_count": 0 + }, + { + "path": "reports/description_optimization_suite.json", + "violation_count": 0 + }, + { + "path": "reports/description_optimization_suite.md", + "violation_count": 0 + }, + { + "path": "reports/eval_suite.json", + "violation_count": 0 + }, + { + "path": "reports/evidence_consistency.json", + "violation_count": 0 + }, + { + "path": "reports/evidence_consistency.md", + "violation_count": 0 + }, + { + "path": "reports/family_summary.md", + "violation_count": 0 + }, + { + "path": "reports/github-benchmark-scan.json", + "violation_count": 0 + }, + { + "path": "reports/github-benchmark-scan.md", + "violation_count": 0 + }, + { + "path": "reports/governance_score.json", + "violation_count": 0 + }, + { + "path": "reports/install_simulation.json", + "violation_count": 0 + }, + { + "path": "reports/install_simulation.md", + "violation_count": 0 + }, + { + "path": "reports/intent-confidence.json", + "violation_count": 0 + }, + { + "path": "reports/intent-confidence.md", + "violation_count": 0 + }, + { + "path": "reports/intent-context.json", + "violation_count": 0 + }, + { + "path": "reports/intent-dialogue.json", + "violation_count": 0 + }, + { + "path": "reports/intent-dialogue.md", + "violation_count": 0 + }, + { + "path": "reports/iteration-directions.json", + "violation_count": 0 + }, + { + "path": "reports/iteration-directions.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/governed-incident-command/bundle.json", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/governed-incident-command/bundle.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/governed-incident-command/review.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/team-frontend-review/bundle.json", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/team-frontend-review/bundle.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/team-frontend-review/review.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/yao-meta-skill/bundle.json", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/yao-meta-skill/bundle.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_bundles/yao-meta-skill/review.md", + "violation_count": 0 + }, + { + "path": "reports/iteration_ledger.md", + "violation_count": 0 + }, + { + "path": "reports/output-risk-profile.json", + "violation_count": 0 + }, + { + "path": "reports/output-risk-profile.md", + "violation_count": 0 + }, + { + "path": "reports/output_blind_answer_key.json", + "violation_count": 0 + }, + { + "path": "reports/output_blind_review_pack.json", + "violation_count": 0 + }, + { + "path": "reports/output_blind_review_pack.md", + "violation_count": 0 + }, + { + "path": "reports/output_execution_runs.json", + "violation_count": 0 + }, + { + "path": "reports/output_execution_runs.md", + "violation_count": 0 + }, + { + "path": "reports/output_quality_scorecard.json", + "violation_count": 0 + }, + { + "path": "reports/output_quality_scorecard.md", + "violation_count": 0 + }, + { + "path": "reports/output_review_adjudication.json", + "violation_count": 0 + }, + { + "path": "reports/output_review_adjudication.md", + "violation_count": 0 + }, + { + "path": "reports/output_review_decisions.json", + "violation_count": 0 + }, + { + "path": "reports/output_review_kit.html", + "violation_count": 0 + }, + { + "path": "reports/output_review_kit.json", + "violation_count": 0 + }, + { + "path": "reports/output_review_kit.md", + "violation_count": 0 + }, + { + "path": "reports/package_verification.json", + "violation_count": 0 + }, + { + "path": "reports/package_verification.md", + "violation_count": 0 + }, + { + "path": "reports/portability_score.json", + "violation_count": 0 + }, + { + "path": "reports/portability_score.md", + "violation_count": 0 + }, + { + "path": "reports/promotion_decisions.json", + "violation_count": 0 + }, + { + "path": "reports/promotion_decisions.md", + "violation_count": 0 + }, + { + "path": "reports/prompt-quality-profile.json", + "violation_count": 0 + }, + { + "path": "reports/prompt-quality-profile.md", + "violation_count": 0 + }, + { + "path": "reports/python_compatibility.json", + "violation_count": 0 + }, + { + "path": "reports/python_compatibility.md", + "violation_count": 0 + }, + { + "path": "reports/reference-scan.json", + "violation_count": 0 + }, + { + "path": "reports/reference-scan.md", + "violation_count": 0 + }, + { + "path": "reports/reference-synthesis.json", + "violation_count": 0 + }, + { + "path": "reports/reference-synthesis.md", + "violation_count": 0 + }, + { + "path": "reports/registry_audit.json", + "violation_count": 0 + }, + { + "path": "reports/registry_audit.md", + "violation_count": 0 + }, + { + "path": "reports/regression_history.md", + "violation_count": 0 + }, + { + "path": "reports/review-studio.html", + "violation_count": 0 + }, + { + "path": "reports/review-studio.json", + "violation_count": 0 + }, + { + "path": "reports/review-viewer.html", + "violation_count": 0 + }, + { + "path": "reports/review-viewer.json", + "violation_count": 0 + }, + { + "path": "reports/review_annotations.json", + "violation_count": 0 + }, + { + "path": "reports/review_annotations.md", + "violation_count": 0 + }, + { + "path": "reports/review_waivers.json", + "violation_count": 0 + }, + { + "path": "reports/review_waivers.md", + "violation_count": 0 + }, + { + "path": "reports/route_scorecard.json", + "violation_count": 0 + }, + { + "path": "reports/route_scorecard.md", + "violation_count": 0 + }, + { + "path": "reports/runtime_permission_probes.json", + "violation_count": 0 + }, + { + "path": "reports/runtime_permission_probes.md", + "violation_count": 0 + }, + { + "path": "reports/security_trust_report.json", + "violation_count": 0 + }, + { + "path": "reports/security_trust_report.md", + "violation_count": 0 + }, + { + "path": "reports/skill-interpretation.html", + "violation_count": 0 + }, + { + "path": "reports/skill-interpretation.json", + "violation_count": 0 + }, + { + "path": "reports/skill-os-2-review.md", + "violation_count": 0 + }, + { + "path": "reports/skill-overview-v2-plan.md", + "violation_count": 0 + }, + { + "path": "reports/skill-overview.html", + "violation_count": 0 + }, + { + "path": "reports/skill-overview.json", + "violation_count": 0 + }, + { + "path": "reports/skill_atlas.html", + "violation_count": 0 + }, + { + "path": "reports/skill_atlas.json", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_audit.json", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_audit.md", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_coverage.json", + "violation_count": 0 + }, + { + "path": "reports/skill_os2_coverage.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-16.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-16.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-17.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-17.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-20.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-20.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-21.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-06-21.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-07-02.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/daily/2026-07-02.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W25.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W25.md", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W27.json", + "violation_count": 0 + }, + { + "path": "reports/skillops/weekly/2026-W27.md", + "violation_count": 0 + }, + { + "path": "reports/system-model.json", + "violation_count": 0 + }, + { + "path": "reports/system-model.md", + "violation_count": 0 + }, + { + "path": "reports/telemetry_hook_recipes.json", + "violation_count": 0 + }, + { + "path": "reports/telemetry_hook_recipes.md", + "violation_count": 0 + }, + { + "path": "reports/upgrade_check.json", + "violation_count": 0 + }, + { + "path": "reports/upgrade_check.md", + "violation_count": 0 + }, + { + "path": "reports/user_patterns.json", + "violation_count": 0 + }, + { + "path": "reports/user_patterns.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_claim_guard.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_intake.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_intake.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_ledger.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_ledger.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_plan.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_plan.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_preflight.html", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_preflight.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_evidence_preflight.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_operator_runbook.html", + "violation_count": 0 + }, + { + "path": "reports/world_class_operator_runbook.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_operator_runbook.md", + "violation_count": 0 + }, + { + "path": "reports/world_class_submission_review.json", + "violation_count": 0 + }, + { + "path": "reports/world_class_submission_review.md", + "violation_count": 0 + }, + { + "path": "security/dependency_policy.md", + "violation_count": 0 + }, + { + "path": "security/network_policy.json", + "violation_count": 0 + }, + { + "path": "security/network_policy.md", + "violation_count": 0 + }, + { + "path": "security/permission_policy.json", + "violation_count": 0 + }, + { + "path": "security/permission_policy.md", + "violation_count": 0 + }, + { + "path": "security/script_policy.md", + "violation_count": 0 + }, + { + "path": "security/secret_policy.md", + "violation_count": 0 + }, + { + "path": "security/trust_policy.md", + "violation_count": 0 + }, + { + "path": "skill-ir/examples/yao-meta-skill.json", + "violation_count": 0 + }, + { + "path": "skill-ir/schema.json", + "violation_count": 0 + } + ], + "violations": [], + "source_ledger": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md" + }, + "artifacts": { + "json": "reports/world_class_claim_guard.json", + "markdown": "reports/world_class_claim_guard.md" + } +} diff --git a/reports/world_class_claim_guard.md b/reports/world_class_claim_guard.md new file mode 100644 index 0000000..a48871b --- /dev/null +++ b/reports/world_class_claim_guard.md @@ -0,0 +1,33 @@ +# World-Class Claim Guard + +Generated at: `2026-07-02` + +## Summary + +- decision: `claim-guard-pass-evidence-pending` +- ledger ready to claim world-class: `false` +- ledger pending evidence: `4` +- claim surfaces scanned: `201` +- JSON claim surfaces scanned: `97` +- metadata claim surfaces scanned: `98` +- package/runtime claim surfaces scanned: `17` +- violations: `0` +- overclaim guard active: `true` + +This guard scans public claim surfaces, machine-readable reports, and package/runtime metadata for completion language that would contradict the world-class evidence ledger. It allows evidence planning and pending-state language, but blocks completion claims until the ledger is ready. + +## Violations + +| Path | Line | Rule | Excerpt | +| --- | ---: | --- | --- | +| `none` | 0 | `none` | none | + +## Rules + +| Rule | Reason | +| --- | --- | +| `ready-to-claim-true` | ready_to_claim_world_class cannot be true while ledger evidence is pending. | +| `world-class-ready-true` | world-class readiness cannot be claimed before accepted external and human evidence exists. | +| `json-world-class-ready-true` | machine-readable claim fields must remain false until the ledger is ready. | +| `completion-phrase` | completion language is blocked until the world-class ledger is accepted. | +| `zh-completion-phrase` | 中文完成态表述必须等到 ledger ready 后才能出现。 | diff --git a/reports/world_class_evidence_intake.json b/reports/world_class_evidence_intake.json new file mode 100644 index 0000000..cf996b4 --- /dev/null +++ b/reports/world_class_evidence_intake.json @@ -0,0 +1,398 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "schema_present": true, + "ledger_entry_count": 4, + "template_count": 4, + "template_pass_count": 4, + "submission_count": 0, + "valid_submission_count": 0, + "invalid_submission_count": 0, + "operator_checklist_count": 4, + "operator_checklist_ready_count": 0, + "valid_packet_source_incomplete_count": 0, + "ready_for_external_collection": true, + "ready_for_ledger_review": false, + "ready_to_claim_world_class": false, + "overclaim_guard_active": true, + "decision": "awaiting-submissions" + }, + "schema": "evidence/world_class/intake.schema.json", + "templates": [ + { + "path": "evidence/world_class/templates/provider-holdout.intake.json", + "evidence_key": "provider-holdout", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 1, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + }, + { + "path": "evidence/world_class/templates/human-adjudication.intake.json", + "evidence_key": "human-adjudication", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 2, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + }, + { + "path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "evidence_key": "native-permission-enforcement", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 3, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + }, + { + "path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "evidence_key": "native-client-telemetry", + "status": "pass", + "template_only": true, + "artifact_integrity": { + "artifact_ref_count": 2, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": 0, + "required_artifact_verified_count": 0 + }, + "errors": [] + } + ], + "submissions": [], + "operator_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": true, + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": false, + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": false, + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "readiness": "awaiting-submission", + "blocking_reason": "No real evidence submission has been provided yet.", + "template_status": "pass", + "submission_status": "missing", + "source_accepted": false, + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ] + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_ledger": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md", + "pending_count": 4 + }, + "artifacts": { + "json": "reports/world_class_evidence_intake.json", + "markdown": "reports/world_class_evidence_intake.md" + } +} diff --git a/reports/world_class_evidence_intake.md b/reports/world_class_evidence_intake.md new file mode 100644 index 0000000..ef76c4e --- /dev/null +++ b/reports/world_class_evidence_intake.md @@ -0,0 +1,265 @@ +# World-Class Evidence Intake + +Generated at: `2026-07-02` + +## Summary + +- decision: `awaiting-submissions` +- schema present: `true` +- templates: `4` / `4` +- submissions: `0` valid / `0` total +- invalid submissions: `0` +- valid packet but source incomplete: `0` +- operator checklist: `0` ready / `4` total +- ready for external collection: `true` +- ready for ledger review: `false` +- ready to claim world-class: `false` +- overclaim guard active: `true` + +This report validates the intake contract for human and external evidence. A valid intake packet means the evidence is ready for ledger review; it does not by itself make a world-class claim true. + +## Templates + +| Evidence | Status | Path | Artifacts | Errors | +| --- | --- | --- | --- | --- | +| `provider-holdout` | `pass` | `evidence/world_class/templates/provider-holdout.intake.json` | 0 existing / 0 sha256 verified / 0 required verified / 1 refs | none | +| `human-adjudication` | `pass` | `evidence/world_class/templates/human-adjudication.intake.json` | 0 existing / 0 sha256 verified / 0 required verified / 2 refs | none | +| `native-permission-enforcement` | `pass` | `evidence/world_class/templates/native-permission-enforcement.intake.json` | 0 existing / 0 sha256 verified / 0 required verified / 3 refs | none | +| `native-client-telemetry` | `pass` | `evidence/world_class/templates/native-client-telemetry.intake.json` | 0 existing / 0 sha256 verified / 0 required verified / 2 refs | none | + +## Submissions + +| Evidence | Status | Path | Artifacts | Errors | +| --- | --- | --- | --- | --- | +| `none` | `n/a` | none | none | none | + +## Operator Checklist + +| Evidence | Readiness | Submission | Next action | +| --- | --- | --- | --- | +| `provider-holdout` | `awaiting-submission` | `missing` | Run provider-backed holdout cases with real credentials and commit only aggregate evidence. | +| `human-adjudication` | `awaiting-submission` | `missing` | Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication. | +| `native-permission-enforcement` | `awaiting-submission` | `missing` | Integrate a real target-client or external installer runtime guard before claiming native permission enforcement. | +| `native-client-telemetry` | `awaiting-submission` | `missing` | Install a real client against the native host and import production metadata-only events. | + +### Provider Holdout + +- readiness: `awaiting-submission` +- blocking reason: No real evidence submission has been provided yet. +- owner: operator with provider credentials +- template: `evidence/world_class/templates/provider-holdout.intake.json` +- submission: `evidence/world_class/submissions/provider-holdout.json` + +#### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- submission_review: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +#### Must Collect + +- provenance_requirements: + - provider-backed model run + - observed timing + - observed token metadata +- success_checks: + - reports/output_execution_runs.json summary.model_executed_count > 0 + - reports/output_execution_runs.json summary.timing_observed_count > 0 + - reports/output_execution_runs.json summary.token_observed_count > 0 + - reports/skill_os2_audit.json item provider-holdout status becomes pass +- evidence_artifacts: + - reports/output_execution_runs.json + - reports/output_execution_runs.md + - reports/skill_os2_audit.json + - evidence/world_class/intake.schema.json + - evidence/world_class/templates/provider-holdout.intake.json + - reports/world_class_evidence_intake.json + - reports/world_class_evidence_intake.md +- privacy_contract: + - Do not commit provider credentials or environment dumps. + - The output execution report records output hashes and aggregate run metadata, not raw provider prompts. + +#### Source Runbook + +- Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. +- For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60 +- For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120 +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Human Adjudication + +- readiness: `awaiting-submission` +- blocking reason: No real evidence submission has been provided yet. +- owner: human reviewer +- template: `evidence/world_class/templates/human-adjudication.intake.json` +- submission: `evidence/world_class/submissions/human-adjudication.json` + +#### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- submission_review: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +#### Must Collect + +- provenance_requirements: + - real reviewer identity + - blind A/B decisions + - answer key unopened until decisions exist +- success_checks: + - reports/output_review_adjudication.json summary.pending_count == 0 + - reports/output_review_adjudication.json summary.judgment_count == summary.pair_count + - reports/output_review_adjudication.json summary.invalid_decision_count == 0 + - reports/output_review_adjudication.json summary.reviewer_metadata_present is true + - reports/output_review_adjudication.json summary.blind_review_attested is true + - reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json + - reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text + - reports/output_review_adjudication.json summary.ready_for_human_evidence is true + - reports/skill_os2_audit.json item human-adjudication status becomes pass +- evidence_artifacts: + - reports/output_blind_review_pack.md + - reports/output_review_kit.md + - reports/output_review_decisions.json + - reports/output_review_adjudication.json + - reports/output_review_adjudication.md + - scripts/import_output_review_decisions.py + - evidence/world_class/intake.schema.json + - evidence/world_class/templates/human-adjudication.intake.json + - reports/world_class_evidence_intake.json + - reports/world_class_evidence_intake.md +- privacy_contract: + - Reviewer decisions should not include raw user data or private customer detail. + - Reviewer reasons must be rubric-based and must not include raw user data or private customer detail. + - The decision importer rejects raw prompt, output, transcript, message, and answer-key fields. + - The adjudication evidence stores prompt_sha256 instead of raw prompt text. + - The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged. + - Keep the answer key separate until after decisions are recorded. + +#### Source Runbook + +- `python3 scripts/yao.py output-review-kit --write-template` +- Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key. +- `python3 scripts/adjudicate_output_review.py --write-template` +- Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only. +- `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- `python3 scripts/yao.py output-review` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Native Permission Enforcement + +- readiness: `awaiting-submission` +- blocking reason: No real evidence submission has been provided yet. +- owner: target client or installer integrator +- template: `evidence/world_class/templates/native-permission-enforcement.intake.json` +- submission: `evidence/world_class/submissions/native-permission-enforcement.json` + +#### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- submission_review: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +#### Must Collect + +- provenance_requirements: + - real target client or external installer runtime guard + - native enforcement flag or externally accepted guard proof + - residual risk retained for fallback targets +- success_checks: + - reports/runtime_permission_probes.json summary.native_enforcement_count > 0 + - reports/runtime_permission_probes.json summary.failure_count == 0 + - reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence + - reports/skill_os2_audit.json item native-permission-enforcement status becomes pass +- evidence_artifacts: + - dist/targets/*/adapter.json + - reports/runtime_permission_probes.json + - reports/runtime_permission_probes.md + - reports/install_simulation.json + - reports/install_simulation.md + - security/permission_policy.json + - evidence/world_class/intake.schema.json + - evidence/world_class/templates/native-permission-enforcement.intake.json + - reports/world_class_evidence_intake.json + - reports/world_class_evidence_intake.md +- privacy_contract: + - Do not mark native_enforcement true for metadata-only fallbacks. + - Keep residual risks visible for targets that still rely on operator enforcement. + +#### Source Runbook + +- Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities. +- Update the generated target adapter only when the guard is actually enforced by that target. +- `python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip` +- `python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation` +- `python3 scripts/yao.py runtime-permissions . --package-dir dist` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Native Client Telemetry + +- readiness: `awaiting-submission` +- blocking reason: No real evidence submission has been provided yet. +- owner: Browser/Chrome/IDE/provider client integrator +- template: `evidence/world_class/templates/native-client-telemetry.intake.json` +- submission: `evidence/world_class/submissions/native-client-telemetry.json` + +#### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- submission_review: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +#### Must Collect + +- provenance_requirements: + - real external client source + - metadata-only event + - local-first import path +- success_checks: + - reports/adoption_drift_report.json summary.source_types.external > 0 + - reports/adoption_drift_report.json summary.adoption_sample_count > 0 + - reports/skill_os2_audit.json item native-client-telemetry status becomes pass +- evidence_artifacts: + - reports/adoption_drift_report.json + - reports/adoption_drift_report.md + - reports/telemetry_hook_recipes.json + - scripts/telemetry_native_host.py + - evidence/world_class/intake.schema.json + - evidence/world_class/templates/native-client-telemetry.intake.json + - reports/world_class_evidence_intake.json + - reports/world_class_evidence_intake.md +- privacy_contract: + - Telemetry must remain metadata-only and local-first. + - Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field. + +#### Source Runbook + +- `python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///` +- Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event. +- `python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl` +- `python3 scripts/yao.py skill-atlas --workspace-root .` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +## Boundary + +- Templates and planned work do not count as accepted evidence. +- Real submissions must include the evidence-key critical artifact paths with verified SHA-256 digests. +- Real submissions must replace template submitter, date, and provenance placeholders with concrete evidence metadata. +- Local command-runner output does not count as provider-backed model evidence. +- Metadata fallback does not count as native permission enforcement. +- Pending reviewer work does not count as human adjudication. diff --git a/reports/world_class_evidence_ledger.json b/reports/world_class_evidence_ledger.json new file mode 100644 index 0000000..c3de04e --- /dev/null +++ b/reports/world_class_evidence_ledger.json @@ -0,0 +1,602 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "decision": "evidence-pending", + "ready_to_claim_world_class": false, + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "summary": { + "ledger_entry_count": 4, + "source_accepted_count": 1, + "accepted_count": 0, + "pending_count": 4, + "human_pending_count": 1, + "external_pending_count": 3, + "submitted_entry_count": 0, + "reviewer_approved_submission_count": 0, + "missing_submission_count": 4, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "submitted_but_pending_count": 0, + "source_accepted_without_valid_submission_count": 1, + "overclaim_guard_active": true, + "ready_to_claim_world_class": false, + "decision": "evidence-pending" + }, + "report_contract": { + "schema_version": "1.0", + "contract": "world-class-evidence-ledger", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "ready_to_claim_world_class", + "ledger_entry_count", + "source_accepted_count", + "accepted_count", + "pending_count", + "human_pending_count", + "external_pending_count", + "submitted_entry_count", + "reviewer_approved_submission_count", + "missing_submission_count", + "invalid_submission_count", + "source_check_count", + "source_pass_count", + "source_blocked_count", + "submitted_but_pending_count", + "source_accepted_without_valid_submission_count", + "overclaim_guard_active" + ], + "source_of_truth": "summary" + }, + "entries": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "pending", + "source_status": "pass", + "source_accepted": true, + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "pending", + "source_status": "human_required", + "source_accepted": false, + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "pending", + "source_status": "external_required", + "source_accepted": false, + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + }, + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_plan": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "task_count": 4, + "evidence_requirement_count": 4 + }, + "submissions": { + "directory": "evidence/world_class/submissions", + "ledger_counts_submission_as_completion": false, + "source_pass_requires_valid_submission": true + }, + "artifacts": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md", + "intake": "reports/world_class_evidence_intake.md" + } +} diff --git a/reports/world_class_evidence_ledger.md b/reports/world_class_evidence_ledger.md new file mode 100644 index 0000000..0dbc802 --- /dev/null +++ b/reports/world_class_evidence_ledger.md @@ -0,0 +1,226 @@ +# World-Class Evidence Ledger + +Generated at: `2026-07-02` + +## Summary + +- decision: `evidence-pending` +- ready to claim world-class: `false` +- entries: `4` +- source accepted: `1` +- source checks: `12` pass / `19` total +- source blocked: `7` +- accepted: `0` +- pending: `4` +- human pending: `1` +- external pending: `3` +- submitted entries: `0` +- reviewer approved submissions: `0` +- submitted but pending: `0` +- source accepted without valid submission: `1` +- invalid submissions: `0` +- overclaim guard active: `true` + +This ledger records the current evidence state. It requires both passing source evidence and a validated intake submission with artifact SHA-256 checks before accepting an item. It does not treat planned work, metadata fallback, pending review, or local command-runner output as world-class completion evidence. + +## Ledger + +| Evidence | Status | Submission | Category | Current | Next action | +| --- | --- | --- | --- | --- | --- | +| `provider-holdout` | `pending` | `missing` | `external` | model-executed 10; token-observed 10 | Run provider-backed holdout cases with real credentials and commit only aggregate evidence. | +| `human-adjudication` | `pending` | `missing` | `human` | 0/5 decisions; pending 5 | Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication. | +| `native-permission-enforcement` | `pending` | `missing` | `external` | native-enforced targets 0; installer-enforced targets 4 | Integrate a real target-client or external installer runtime guard before claiming native permission enforcement. | +| `native-client-telemetry` | `pending` | `missing` | `external` | external source events 0; adoption samples 1 | Install a real client against the native host and import production metadata-only events. | + +## Provider Holdout + +- objective: Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata. +- source status: `pass` +- observed state: `{"model_executed_count": 10, "timing_observed_count": 10, "token_observed_count": 10, "accepted": true}` +- source checks: `3` pass / `3` total +- submission state: `{"status": "missing", "path": "evidence/world_class/submissions/provider-holdout.json", "artifact_ref_count": 0, "attested_real_evidence": false, "privacy_contract_satisfied": false, "ledger_reviewer_approved": false, "ledger_reviewer": "", "ledger_reviewed_at": "", "ledger_counts_as_completion": false}` + +### Provenance Requirements + +- provider-backed model run +- observed timing +- observed token metadata + +### Source Runbook + +- Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. +- For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60 +- For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120 +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Source Evidence Checks + +| Check | Current | Expected | Status | +| --- | --- | --- | --- | +| Provider model run | `10` | `>0` | `pass` | +| Timing observed | `10` | `>0` | `pass` | +| Token usage observed | `10` | `>0` | `pass` | + +### Completion Assertions + +- reports/output_execution_runs.json summary.model_executed_count > 0 +- reports/output_execution_runs.json summary.timing_observed_count > 0 +- reports/output_execution_runs.json summary.token_observed_count > 0 +- reports/skill_os2_audit.json item provider-holdout status becomes pass + +### Privacy Contract + +- Do not commit provider credentials or environment dumps. +- The output execution report records output hashes and aggregate run metadata, not raw provider prompts. + +## Human Adjudication + +- objective: Record real blind A/B reviewer decisions before claiming human output review completion. +- source status: `human_required` +- observed state: `{"pair_count": 5, "judgment_count": 0, "pending_count": 5, "invalid_decision_count": 0, "answer_revealed_count": 0, "reviewer_metadata_present": false, "reason_required": true, "blind_review_attested": false, "raw_content_excluded_attested": true, "reviewer_reason_required_attested": true, "ready_for_human_evidence": false, "raw_content_allowed": false, "raw_content_path_count": 0, "accepted": false}` +- source checks: `5` pass / `10` total +- submission state: `{"status": "missing", "path": "evidence/world_class/submissions/human-adjudication.json", "artifact_ref_count": 0, "attested_real_evidence": false, "privacy_contract_satisfied": false, "ledger_reviewer_approved": false, "ledger_reviewer": "", "ledger_reviewed_at": "", "ledger_counts_as_completion": false}` + +### Provenance Requirements + +- real reviewer identity +- blind A/B decisions +- answer key unopened until decisions exist + +### Source Runbook + +- `python3 scripts/yao.py output-review-kit --write-template` +- Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key. +- `python3 scripts/adjudicate_output_review.py --write-template` +- Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only. +- `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- `python3 scripts/yao.py output-review` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Source Evidence Checks + +| Check | Current | Expected | Status | +| --- | --- | --- | --- | +| Review pairs exist | `5` | `>0` | `pass` | +| No pending decisions | `5` | `==0` | `blocked` | +| Judgments complete | `0` | `==pair_count` | `blocked` | +| No invalid decisions | `0` | `==0` | `pass` | +| Reviewer metadata | `False` | `true` | `blocked` | +| Reason required | `True` | `true` | `pass` | +| Blind review attested | `False` | `true` | `blocked` | +| Raw content attested | `True` | `true` | `pass` | +| Raw content blocked | `False` | `false` | `pass` | +| Human evidence ready | `False` | `true` | `blocked` | + +### Completion Assertions + +- reports/output_review_adjudication.json summary.pending_count == 0 +- reports/output_review_adjudication.json summary.judgment_count == summary.pair_count +- reports/output_review_adjudication.json summary.invalid_decision_count == 0 +- reports/output_review_adjudication.json summary.reviewer_metadata_present is true +- reports/output_review_adjudication.json summary.blind_review_attested is true +- reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json +- reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text +- reports/output_review_adjudication.json summary.ready_for_human_evidence is true +- reports/skill_os2_audit.json item human-adjudication status becomes pass + +### Privacy Contract + +- Reviewer decisions should not include raw user data or private customer detail. +- Reviewer reasons must be rubric-based and must not include raw user data or private customer detail. +- The decision importer rejects raw prompt, output, transcript, message, and answer-key fields. +- The adjudication evidence stores prompt_sha256 instead of raw prompt text. +- The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged. +- Keep the answer key separate until after decisions are recorded. + +## Native Permission Enforcement + +- objective: Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities. +- source status: `external_required` +- observed state: `{"native_enforcement_count": 0, "metadata_fallback_count": 4, "installer_enforcement_pass_count": 4, "installer_permission_failure_count": 0, "installer_enforcement_ready": true, "residual_risk_count": 4, "failure_count": 0, "accepted": false}` +- source checks: `2` pass / `3` total +- submission state: `{"status": "missing", "path": "evidence/world_class/submissions/native-permission-enforcement.json", "artifact_ref_count": 0, "attested_real_evidence": false, "privacy_contract_satisfied": false, "ledger_reviewer_approved": false, "ledger_reviewer": "", "ledger_reviewed_at": "", "ledger_counts_as_completion": false}` + +### Provenance Requirements + +- real target client or external installer runtime guard +- native enforcement flag or externally accepted guard proof +- residual risk retained for fallback targets + +### Source Runbook + +- Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities. +- Update the generated target adapter only when the guard is actually enforced by that target. +- `python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip` +- `python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation` +- `python3 scripts/yao.py runtime-permissions . --package-dir dist` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Source Evidence Checks + +| Check | Current | Expected | Status | +| --- | --- | --- | --- | +| Native enforcement | `0` | `>0` | `blocked` | +| Probe failures | `0` | `==0` | `pass` | +| Installer support | `True` | `true` | `pass` | + +### Completion Assertions + +- reports/runtime_permission_probes.json summary.native_enforcement_count > 0 +- reports/runtime_permission_probes.json summary.failure_count == 0 +- reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence +- reports/skill_os2_audit.json item native-permission-enforcement status becomes pass + +### Privacy Contract + +- Do not mark native_enforcement true for metadata-only fallbacks. +- Keep residual risks visible for targets that still rely on operator enforcement. + +## Native Client Telemetry + +- objective: Import production metadata-only events from a real external client into the local drift loop. +- source status: `external_required` +- observed state: `{"external_source_events": 0, "adoption_sample_count": 1, "raw_content_allowed": false, "risk_band": "low", "accepted": false}` +- source checks: `2` pass / `3` total +- submission state: `{"status": "missing", "path": "evidence/world_class/submissions/native-client-telemetry.json", "artifact_ref_count": 0, "attested_real_evidence": false, "privacy_contract_satisfied": false, "ledger_reviewer_approved": false, "ledger_reviewer": "", "ledger_reviewed_at": "", "ledger_counts_as_completion": false}` + +### Provenance Requirements + +- real external client source +- metadata-only event +- local-first import path + +### Source Runbook + +- `python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///` +- Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event. +- `python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl` +- `python3 scripts/yao.py skill-atlas --workspace-root .` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Source Evidence Checks + +| Check | Current | Expected | Status | +| --- | --- | --- | --- | +| External events | `0` | `>0` | `blocked` | +| Adoption sample | `1` | `>0` | `pass` | +| Raw content blocked | `False` | `false` | `pass` | + +### Completion Assertions + +- reports/adoption_drift_report.json summary.source_types.external > 0 +- reports/adoption_drift_report.json summary.adoption_sample_count > 0 +- reports/skill_os2_audit.json item native-client-telemetry status becomes pass + +### Privacy Contract + +- Telemetry must remain metadata-only and local-first. +- Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field. diff --git a/reports/world_class_evidence_plan.json b/reports/world_class_evidence_plan.json new file mode 100644 index 0000000..1219996 --- /dev/null +++ b/reports/world_class_evidence_plan.json @@ -0,0 +1,372 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "audit_decision": "continue-iteration", + "world_class_ready": false, + "audit_world_class_ready": false, + "ready_to_claim_world_class": false, + "ledger_completion_required": true, + "evidence_requirement_count": 4, + "task_count": 4, + "human_task_count": 1, + "external_task_count": 3, + "review_task_count": 0, + "decision": "collect-external-evidence" + }, + "tasks": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "status": "pass", + "category": "external", + "owner": "operator with provider credentials", + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "audit_next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "category": "human", + "owner": "human reviewer", + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "audit_next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "category": "external", + "owner": "target client or installer integrator", + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "audit_next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "audit_next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "evidence_requirements": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "status": "pass", + "category": "external", + "owner": "operator with provider credentials", + "current": "model-executed 10; token-observed 10", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "audit_next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "status": "human_required", + "category": "human", + "owner": "human reviewer", + "current": "0/5 decisions; pending 5", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "audit_next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "status": "external_required", + "category": "external", + "owner": "target client or installer integrator", + "current": "native-enforced targets 0; installer-enforced targets 4", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "audit_next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "key": "native-client-telemetry", + "label": "Native Client Telemetry", + "status": "external_required", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "current": "external source events 0; adoption samples 1", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "audit_next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "source_audit": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "open_gap_count": 3 + }, + "artifacts": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "ledger": "reports/world_class_evidence_ledger.md", + "intake": "reports/world_class_evidence_intake.md" + } +} diff --git a/reports/world_class_evidence_plan.md b/reports/world_class_evidence_plan.md new file mode 100644 index 0000000..b2f1275 --- /dev/null +++ b/reports/world_class_evidence_plan.md @@ -0,0 +1,190 @@ +# World-Class Evidence Plan + +Generated at: `2026-07-02` + +## Summary + +- decision: `collect-external-evidence` +- audit decision: `continue-iteration` +- ready to claim world-class: `false` +- ledger completion required: `true` +- evidence requirements: `4` +- tasks: `4` +- human tasks: `1` +- external tasks: `3` + +This report is an execution plan for the remaining world-class evidence gaps. It does not count a plan or source-report pass as completion; the ledger must still validate accepted submissions. + +## Task Table + +| Task | Status | Category | Owner | Current | +| --- | --- | --- | --- | --- | +| `provider-holdout` | `pass` | `external` | operator with provider credentials | model-executed 10; token-observed 10 | +| `human-adjudication` | `human_required` | `human` | human reviewer | 0/5 decisions; pending 5 | +| `native-permission-enforcement` | `external_required` | `external` | target client or installer integrator | native-enforced targets 0; installer-enforced targets 4 | +| `native-client-telemetry` | `external_required` | `external` | Browser/Chrome/IDE/provider client integrator | external source events 0; adoption samples 1 | + +## Provider Holdout + +- objective: Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata. +- audit next action: Run provider-backed holdout cases with real credentials and commit only aggregate evidence. + +### Runbook + +- Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. +- For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60 +- For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120 +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Success Checks + +- reports/output_execution_runs.json summary.model_executed_count > 0 +- reports/output_execution_runs.json summary.timing_observed_count > 0 +- reports/output_execution_runs.json summary.token_observed_count > 0 +- reports/skill_os2_audit.json item provider-holdout status becomes pass + +### Evidence Artifacts + +- `reports/output_execution_runs.json` +- `reports/output_execution_runs.md` +- `reports/skill_os2_audit.json` +- `evidence/world_class/intake.schema.json` +- `evidence/world_class/templates/provider-holdout.intake.json` +- `reports/world_class_evidence_intake.json` +- `reports/world_class_evidence_intake.md` + +### Privacy Contract + +- Do not commit provider credentials or environment dumps. +- The output execution report records output hashes and aggregate run metadata, not raw provider prompts. + +## Human Adjudication + +- objective: Record real blind A/B reviewer decisions before claiming human output review completion. +- audit next action: Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication. + +### Runbook + +- `python3 scripts/yao.py output-review-kit --write-template` +- Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key. +- `python3 scripts/adjudicate_output_review.py --write-template` +- Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only. +- `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication` +- `python3 scripts/yao.py output-review` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Success Checks + +- reports/output_review_adjudication.json summary.pending_count == 0 +- reports/output_review_adjudication.json summary.judgment_count == summary.pair_count +- reports/output_review_adjudication.json summary.invalid_decision_count == 0 +- reports/output_review_adjudication.json summary.reviewer_metadata_present is true +- reports/output_review_adjudication.json summary.blind_review_attested is true +- reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json +- reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text +- reports/output_review_adjudication.json summary.ready_for_human_evidence is true +- reports/skill_os2_audit.json item human-adjudication status becomes pass + +### Evidence Artifacts + +- `reports/output_blind_review_pack.md` +- `reports/output_review_kit.md` +- `reports/output_review_decisions.json` +- `reports/output_review_adjudication.json` +- `reports/output_review_adjudication.md` +- `scripts/import_output_review_decisions.py` +- `evidence/world_class/intake.schema.json` +- `evidence/world_class/templates/human-adjudication.intake.json` +- `reports/world_class_evidence_intake.json` +- `reports/world_class_evidence_intake.md` + +### Privacy Contract + +- Reviewer decisions should not include raw user data or private customer detail. +- Reviewer reasons must be rubric-based and must not include raw user data or private customer detail. +- The decision importer rejects raw prompt, output, transcript, message, and answer-key fields. +- The adjudication evidence stores prompt_sha256 instead of raw prompt text. +- The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged. +- Keep the answer key separate until after decisions are recorded. + +## Native Permission Enforcement + +- objective: Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities. +- audit next action: Integrate a real target-client or external installer runtime guard before claiming native permission enforcement. + +### Runbook + +- Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities. +- Update the generated target adapter only when the guard is actually enforced by that target. +- `python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip` +- `python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation` +- `python3 scripts/yao.py runtime-permissions . --package-dir dist` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Success Checks + +- reports/runtime_permission_probes.json summary.native_enforcement_count > 0 +- reports/runtime_permission_probes.json summary.failure_count == 0 +- reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence +- reports/skill_os2_audit.json item native-permission-enforcement status becomes pass + +### Evidence Artifacts + +- `dist/targets/*/adapter.json` +- `reports/runtime_permission_probes.json` +- `reports/runtime_permission_probes.md` +- `reports/install_simulation.json` +- `reports/install_simulation.md` +- `security/permission_policy.json` +- `evidence/world_class/intake.schema.json` +- `evidence/world_class/templates/native-permission-enforcement.intake.json` +- `reports/world_class_evidence_intake.json` +- `reports/world_class_evidence_intake.md` + +### Privacy Contract + +- Do not mark native_enforcement true for metadata-only fallbacks. +- Keep residual risks visible for targets that still rely on operator enforcement. + +## Native Client Telemetry + +- objective: Import production metadata-only events from a real external client into the local drift loop. +- audit next action: Install a real client against the native host and import production metadata-only events. + +### Runbook + +- `python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///` +- Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event. +- `python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl` +- `python3 scripts/yao.py skill-atlas --workspace-root .` +- `python3 scripts/yao.py skill-os2-audit . --generated-at ` +- Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields. +- `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` + +### Success Checks + +- reports/adoption_drift_report.json summary.source_types.external > 0 +- reports/adoption_drift_report.json summary.adoption_sample_count > 0 +- reports/skill_os2_audit.json item native-client-telemetry status becomes pass + +### Evidence Artifacts + +- `reports/adoption_drift_report.json` +- `reports/adoption_drift_report.md` +- `reports/telemetry_hook_recipes.json` +- `scripts/telemetry_native_host.py` +- `evidence/world_class/intake.schema.json` +- `evidence/world_class/templates/native-client-telemetry.intake.json` +- `reports/world_class_evidence_intake.json` +- `reports/world_class_evidence_intake.md` + +### Privacy Contract + +- Telemetry must remain metadata-only and local-first. +- Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field. diff --git a/reports/world_class_evidence_preflight.html b/reports/world_class_evidence_preflight.html new file mode 100644 index 0000000..3f03cb5 --- /dev/null +++ b/reports/world_class_evidence_preflight.html @@ -0,0 +1,975 @@ + + + + + + World-Class Evidence Preflight + + + + +
    +
    + Evidence Collection +

    World-Class Evidence Preflight

    +

    This operator view shows which external and human evidence is still blocked, which commands prepare editable submission drafts, and why preflight never counts as accepted evidence.

    +
    Decisioncollection-preflight-blocked
    Pending4
    Ready1
    Blocked3
    Source12/19
    Repairs11/11
    Phases2/2
    +
    +
    +
    +

    Submission Kit

    +

    Generate drafts only after real provider, human-review, native-permission, or native-client work exists. Drafts remain non-evidence until valid aggregate artifact refs and SHA-256 digests are supplied. Artifact prefill is convenience data only.

    +
      +
    • submissions directory: evidence/world_class/submissions
    • +
    • drafts count as evidence: false
    • +
    • artifact prefill counts as evidence: false
    • +
    • preflight accepts evidence: false
    • +
    +
    +
    + Submission refs +

    submission-ref

    + 7/7 ready +

    Rows marked submission-ref are the aggregate paths expected in artifact_refs.

    + copy to artifact_refs: true +
    + +
    + Supporting evidence +

    supporting-evidence

    + 31/31 ready +

    Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.

    + copy to artifact_refs: false +
    +
    +

    submission-ref rows are the paths expected in artifact_refs; supporting-evidence rows stay available for audit context.

    +
    + +
    +
    +

    Phase Queue

    +

    Repair rows are grouped into execution phases so operators can clear the first blocker before moving to later source collection. Phase queue rows remain procedural and never count as accepted evidence.

    +
    +
    +
    + #20 · blocked +

    Unblock access

    +
    +
    +
    Phase
    unblock-access
    +
    Rows
    4 / 4 blocked
    +
    Owners
    Browser/Chrome/IDE/provider client integrator, human reviewer, operator with provider credentials, target client or installer integrator
    +
    Evidence
    human-adjudication, native-client-telemetry, native-permission-enforcement, provider-holdout
    +
    Next
    Assign a real reviewer identity before claiming human adjudication.
    +
    Verify
    python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Counts
    does not count as completion
    +
    +
    + +
    +
    + #40 · blocked +

    Collect source

    +
    +
    +
    Phase
    collect-source
    +
    Rows
    7 / 7 blocked
    +
    Owners
    Browser/Chrome/IDE/provider client integrator, human reviewer, target client or installer integrator
    +
    Evidence
    human-adjudication, native-client-telemetry, native-permission-enforcement
    +
    Next
    Set reviewer_attestation only after choices are completed before opening the answer key.
    +
    Verify
    python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Counts
    does not count as completion
    +
    +
    +
    +

    Evidence Queue

    +
    +
    +
    + external · blocked +

    Provider Holdout

    +
    +
    +
    Evidence
    provider-holdout
    +
    Ledger
    pending
    +
    Intake
    awaiting-submission
    +
    Review
    awaiting-submission
    +
    Draft
    evidence/world_class/submissions/provider-holdout.json
    +
    +
    +

    Next Action

    +

    Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.

    + python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions + python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts +
    +
    +

    Artifact Roles

    +
    +
    + Submission refs +

    submission-ref

    + 1/1 ready +

    Rows marked submission-ref are the aggregate paths expected in artifact_refs.

    + copy to artifact_refs: true +
    + +
    + Supporting evidence +

    supporting-evidence

    + 6/6 ready +

    Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.

    + copy to artifact_refs: false +
    +
    +
    +
    +

    Prechecks

    +
    +
    +
    + file + Output eval cases +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Keep output holdout cases available before provider execution.
    +
    +
    + +
    +
    + file + Provider runner +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Use the provider runner instead of the local command runner for model-backed evidence.
    +
    +
    + +
    +
    + env_any + Provider credential +
    +
    +
    Current
    not-set
    +
    Status
    missing
    +
    Action
    Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    +
    +
    + +
    +
    + env + Provider model +
    +
    +
    Current
    not-set
    +
    Status
    optional
    +
    Action
    Optionally set YAO_OUTPUT_EVAL_MODEL, or pass --provider-model for the selected provider.
    +
    +
    +
    +
    +
    +

    Source Checks

    +
    +
    +
    + model_executed_count + Provider model run +
    +
    +
    Current
    10
    +
    Expected
    >0
    +
    Status
    pass
    +
    Action
    Run provider-backed output-exec with real credentials.
    +
    +
    + +
    +
    + timing_observed_count + Timing observed +
    +
    +
    Current
    10
    +
    Expected
    >0
    +
    Status
    pass
    +
    Action
    Provider execution should record timing metadata.
    +
    +
    + +
    +
    + token_observed_count + Token usage observed +
    +
    +
    Current
    10
    +
    Expected
    >0
    +
    Status
    pass
    +
    Action
    Provider execution should return non-estimated token usage.
    +
    +
    +
    +
    +
    +

    Repair Rows

    +
    +
    +
    + precheck + provider-api-key +
    +
    +
    Priority
    20
    +
    Phase
    unblock-access
    +
    Owner
    operator with provider credentials
    +
    Status
    blocked
    +
    Reason
    Required env_any precheck is missing.
    +
    Action
    Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    +
    Verify
    python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    +
    +
    +
    +

    Runbook

    +
    • Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    • For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60
    • For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +
    +
    + +
    +
    + human · ready-for-human-review +

    Human Adjudication

    +
    +
    +
    Evidence
    human-adjudication
    +
    Ledger
    pending
    +
    Intake
    awaiting-submission
    +
    Review
    awaiting-submission
    +
    Draft
    evidence/world_class/submissions/human-adjudication.json
    +
    +
    +

    Next Action

    +

    Assign a real reviewer identity before claiming human adjudication.

    + python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions + python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions --prefill-artifacts +
    +
    +

    Artifact Roles

    +
    +
    + Submission refs +

    submission-ref

    + 2/2 ready +

    Rows marked submission-ref are the aggregate paths expected in artifact_refs.

    + copy to artifact_refs: true +
    + +
    + Supporting evidence +

    supporting-evidence

    + 8/8 ready +

    Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.

    + copy to artifact_refs: false +
    +
    +
    +
    +

    Prechecks

    +
    +
    +
    + file + Blind review kit +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Open the blind review kit and record real reviewer choices with required rationale.
    +
    +
    + +
    +
    + file + Decision template +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Import real A/B decisions with reviewer, reviewed_at, winner_variant, confidence, reason, and truthful blind-review attestation via `python3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication`.
    +
    +
    + +
    +
    + file + Decision importer +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Use the importer to reject raw content fields and normalize reviewer decisions before adjudication.
    +
    +
    + +
    +
    + human + Human reviewer +
    +
    +
    Current
    external-human-action
    +
    Status
    human-required
    +
    Action
    Assign a real reviewer identity before claiming human adjudication.
    +
    +
    +
    +
    +
    +

    Source Checks

    +
    +
    +
    + pair_count + Review pairs exist +
    +
    +
    Current
    5
    +
    Expected
    >0
    +
    Status
    pass
    +
    Action
    Generate the blind A/B review pack.
    +
    +
    + +
    +
    + pending_count + No pending decisions +
    +
    +
    Current
    5
    +
    Expected
    ==0
    +
    Status
    blocked
    +
    Action
    Record a reviewer choice and reason for every pair.
    +
    +
    + +
    +
    + judgment_count + Judgments complete +
    +
    +
    Current
    0
    +
    Expected
    ==pair_count
    +
    Status
    blocked
    +
    Action
    Every pair needs one valid human judgment.
    +
    +
    + +
    +
    + invalid_decision_count + No invalid decisions +
    +
    +
    Current
    0
    +
    Expected
    ==0
    +
    Status
    pass
    +
    Action
    Fix malformed winner/confidence entries.
    +
    +
    + +
    +
    + reviewer_metadata_present + Reviewer metadata +
    +
    +
    Current
    False
    +
    Expected
    true
    +
    Status
    blocked
    +
    Action
    Record reviewer and reviewed_at before adjudication can count.
    +
    +
    + +
    +
    + reason_required + Reason required +
    +
    +
    Current
    True
    +
    Expected
    true
    +
    Status
    pass
    +
    Action
    Keep reason mandatory for every imported or direct reviewer decision.
    +
    +
    + +
    +
    + blind_review_attested + Blind review attested +
    +
    +
    Current
    False
    +
    Expected
    true
    +
    Status
    blocked
    +
    Action
    Set reviewer_attestation only after choices are completed before opening the answer key.
    +
    +
    + +
    +
    + raw_content_excluded_attested + Raw content attested +
    +
    +
    Current
    True
    +
    Expected
    true
    +
    Status
    pass
    +
    Action
    Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content.
    +
    +
    + +
    +
    + raw_content_allowed + Raw content blocked +
    +
    +
    Current
    False
    +
    Expected
    false
    +
    Status
    pass
    +
    Action
    Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages.
    +
    +
    + +
    +
    + ready_for_human_evidence + Human evidence ready +
    +
    +
    Current
    False
    +
    Expected
    true
    +
    Status
    blocked
    +
    Action
    Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.
    +
    +
    +
    +
    +
    +

    Repair Rows

    +
    +
    +
    + precheck + human-reviewer +
    +
    +
    Priority
    20
    +
    Phase
    unblock-access
    +
    Owner
    human reviewer
    +
    Status
    blocked
    +
    Reason
    Required human precheck is human-required.
    +
    Action
    Assign a real reviewer identity before claiming human adjudication.
    +
    Verify
    python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    + +
    +
    + source-check + blind_review_attested +
    +
    +
    Priority
    40
    +
    Phase
    collect-source
    +
    Owner
    human reviewer
    +
    Status
    blocked
    +
    Reason
    Current value False does not satisfy 'true'.
    +
    Action
    Set reviewer_attestation only after choices are completed before opening the answer key.
    +
    Verify
    python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    + +
    +
    + source-check + judgment_count +
    +
    +
    Priority
    40
    +
    Phase
    collect-source
    +
    Owner
    human reviewer
    +
    Status
    blocked
    +
    Reason
    Current value 0 does not satisfy '==pair_count'.
    +
    Action
    Every pair needs one valid human judgment.
    +
    Verify
    python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    + +
    +
    + source-check + pending_count +
    +
    +
    Priority
    40
    +
    Phase
    collect-source
    +
    Owner
    human reviewer
    +
    Status
    blocked
    +
    Reason
    Current value 5 does not satisfy '==0'.
    +
    Action
    Record a reviewer choice and reason for every pair.
    +
    Verify
    python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    + +
    +
    + source-check + ready_for_human_evidence +
    +
    +
    Priority
    40
    +
    Phase
    collect-source
    +
    Owner
    human reviewer
    +
    Status
    blocked
    +
    Reason
    Current value False does not satisfy 'true'.
    +
    Action
    Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.
    +
    Verify
    python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    + +
    +
    + source-check + reviewer_metadata_present +
    +
    +
    Priority
    40
    +
    Phase
    collect-source
    +
    Owner
    human reviewer
    +
    Status
    blocked
    +
    Reason
    Current value False does not satisfy 'true'.
    +
    Action
    Record reviewer and reviewed_at before adjudication can count.
    +
    Verify
    python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    +
    +
    +
    +

    Runbook

    +
    • python3 scripts/yao.py output-review-kit --write-template
    • Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.
    • python3 scripts/adjudicate_output_review.py --write-template
    • Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.
    • python3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • python3 scripts/yao.py output-review
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +
    +
    + +
    +
    + external · blocked +

    Native Permission Enforcement

    +
    +
    +
    Evidence
    native-permission-enforcement
    +
    Ledger
    pending
    +
    Intake
    awaiting-submission
    +
    Review
    awaiting-submission
    +
    Draft
    evidence/world_class/submissions/native-permission-enforcement.json
    +
    +
    +

    Next Action

    +

    Attach a real target-client or external installer runtime guard; metadata fallback is not enough.

    + python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions + python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions --prefill-artifacts +
    +
    +

    Artifact Roles

    +
    +
    + Submission refs +

    submission-ref

    + 2/2 ready +

    Rows marked submission-ref are the aggregate paths expected in artifact_refs.

    + copy to artifact_refs: true +
    + +
    + Supporting evidence +

    supporting-evidence

    + 11/11 ready +

    Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.

    + copy to artifact_refs: false +
    +
    +
    +
    +

    Prechecks

    +
    +
    +
    + file + Permission policy +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Keep approved high-permission capabilities explicit.
    +
    +
    + +
    +
    + file + Runtime probes +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Refresh runtime permission probes after packaging changes.
    +
    +
    + +
    +
    + external + Native guard +
    +
    +
    Current
    external-integration-required
    +
    Status
    external-required
    +
    Action
    Attach a real target-client or external installer runtime guard; metadata fallback is not enough.
    +
    +
    +
    +
    +
    +

    Source Checks

    +
    +
    +
    + native_enforcement_count + Native enforcement +
    +
    +
    Current
    0
    +
    Expected
    >0
    +
    Status
    blocked
    +
    Action
    Collect real target-client or external runtime guard proof.
    +
    +
    + +
    +
    + failure_count + Probe failures +
    +
    +
    Current
    0
    +
    Expected
    ==0
    +
    Status
    pass
    +
    Action
    Runtime permission probes must stay clean.
    +
    +
    + +
    +
    + installer_enforcement_ready + Installer support +
    +
    +
    Current
    True
    +
    Expected
    true
    +
    Status
    pass
    +
    Action
    Installer enforcement is supporting evidence, not native proof.
    +
    +
    +
    +
    +
    +

    Repair Rows

    +
    +
    +
    + precheck + native-guard +
    +
    +
    Priority
    20
    +
    Phase
    unblock-access
    +
    Owner
    target client or installer integrator
    +
    Status
    blocked
    +
    Reason
    Required external precheck is external-required.
    +
    Action
    Attach a real target-client or external installer runtime guard; metadata fallback is not enough.
    +
    Verify
    python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    + +
    +
    + source-check + native_enforcement_count +
    +
    +
    Priority
    40
    +
    Phase
    collect-source
    +
    Owner
    target client or installer integrator
    +
    Status
    blocked
    +
    Reason
    Current value 0 does not satisfy '>0'.
    +
    Action
    Collect real target-client or external runtime guard proof.
    +
    Verify
    python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    +
    +
    +
    +

    Runbook

    +
    • Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.
    • Update the generated target adapter only when the guard is actually enforced by that target.
    • python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip
    • python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation
    • python3 scripts/yao.py runtime-permissions . --package-dir dist
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +
    +
    + +
    +
    + external · blocked +

    Native Client Telemetry

    +
    +
    +
    Evidence
    native-client-telemetry
    +
    Ledger
    pending
    +
    Intake
    awaiting-submission
    +
    Review
    awaiting-submission
    +
    Draft
    evidence/world_class/submissions/native-client-telemetry.json
    +
    +
    +

    Next Action

    +

    Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.

    + python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions + python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions --prefill-artifacts +
    +
    +

    Artifact Roles

    +
    +
    + Submission refs +

    submission-ref

    + 2/2 ready +

    Rows marked submission-ref are the aggregate paths expected in artifact_refs.

    + copy to artifact_refs: true +
    + +
    + Supporting evidence +

    supporting-evidence

    + 6/6 ready +

    Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.

    + copy to artifact_refs: false +
    +
    +
    +
    +

    Prechecks

    +
    +
    +
    + file + Native telemetry host +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Use the native host to receive metadata-only client events.
    +
    +
    + +
    +
    + file + Hook recipes +
    +
    +
    Current
    present
    +
    Status
    pass
    +
    Action
    Refresh telemetry hook recipes before external client installation.
    +
    +
    + +
    +
    + external + External client +
    +
    +
    Current
    external-integration-required
    +
    Status
    external-required
    +
    Action
    Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.
    +
    +
    +
    +
    +
    +

    Source Checks

    +
    +
    +
    + external_source_events + External events +
    +
    +
    Current
    0
    +
    Expected
    >0
    +
    Status
    blocked
    +
    Action
    Import at least one metadata-only event from a real client.
    +
    +
    + +
    +
    + adoption_sample_count + Adoption sample +
    +
    +
    Current
    1
    +
    Expected
    >0
    +
    Status
    pass
    +
    Action
    Telemetry must include adoption outcome evidence.
    +
    +
    + +
    +
    + raw_content_allowed + Raw content blocked +
    +
    +
    Current
    False
    +
    Expected
    false
    +
    Status
    pass
    +
    Action
    Telemetry must stay metadata-only.
    +
    +
    +
    +
    +
    +

    Repair Rows

    +
    +
    +
    + precheck + external-client +
    +
    +
    Priority
    20
    +
    Phase
    unblock-access
    +
    Owner
    Browser/Chrome/IDE/provider client integrator
    +
    Status
    blocked
    +
    Reason
    Required external precheck is external-required.
    +
    Action
    Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.
    +
    Verify
    python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    + +
    +
    + source-check + external_source_events +
    +
    +
    Priority
    40
    +
    Phase
    collect-source
    +
    Owner
    Browser/Chrome/IDE/provider client integrator
    +
    Status
    blocked
    +
    Reason
    Current value 0 does not satisfy '>0'.
    +
    Action
    Import at least one metadata-only event from a real client.
    +
    Verify
    python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions
    +
    Evidence
    does not count as completion
    +
    +
    +
    +
    +
    +

    Runbook

    +
    • python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension://<extension-id>/
    • Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.
    • python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl
    • python3 scripts/yao.py skill-atlas --workspace-root .
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +
    +
    +
    +
    +
    +

    Safety Boundary

    +
    • Environment variables are displayed only as set or not-set; secret values are never printed.
    • Human-required and external-required states are operator work, not accepted evidence.
    • The world-class ledger remains the only source of truth for ready_to_claim_world_class.
    +
    +
    + + diff --git a/reports/world_class_evidence_preflight.json b/reports/world_class_evidence_preflight.json new file mode 100644 index 0000000..996b080 --- /dev/null +++ b/reports/world_class_evidence_preflight.json @@ -0,0 +1,1978 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "evidence_item_count": 4, + "precheck_count": 14, + "precheck_pass_count": 9, + "precheck_missing_count": 1, + "precheck_optional_count": 1, + "precheck_human_required_count": 1, + "precheck_external_required_count": 2, + "collection_ready_count": 1, + "collection_blocked_count": 3, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "repair_checklist_count": 11, + "repair_blocked_count": 11, + "repair_ready_count": 0, + "repair_phase_counts": { + "unblock-access": 4, + "collect-source": 7 + }, + "next_repair_action_id": "human-adjudication-precheck-human-reviewer", + "next_repair_phase": "unblock-access", + "next_repair_owner": "human reviewer", + "next_repair_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "repair_counts_as_completion": false, + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false, + "pending_count": 4, + "ready_to_claim_world_class": false, + "credential_value_exposed": false, + "preflight_counts_as_evidence": false, + "decision": "collection-preflight-blocked" + }, + "status_counts": { + "blocked": 3, + "ready-for-human-review": 1 + }, + "precheck_status_counts": { + "pass": 9, + "missing": 1, + "optional": 1, + "human-required": 1, + "external-required": 2 + }, + "items": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "status": "blocked", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": true, + "prechecks": [ + { + "evidence_key": "provider-holdout", + "key": "output-cases", + "label": "Output eval cases", + "kind": "file", + "required": true, + "next_action": "Keep output holdout cases available before provider execution.", + "secret_value_redacted": false, + "path": "evals/output/cases.jsonl", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-runner", + "label": "Provider runner", + "kind": "file", + "required": true, + "next_action": "Use the provider runner instead of the local command runner for model-backed evidence.", + "secret_value_redacted": false, + "path": "scripts/provider_output_eval_runner.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-api-key", + "label": "Provider credential", + "kind": "env_any", + "required": true, + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "secret_value_redacted": true, + "env_any": [ + "OPENAI_API_KEY", + "DEEPSEEK_API_KEY" + ], + "status": "missing", + "actual": "not-set", + "set_count": 0, + "set_envs": [] + }, + { + "evidence_key": "provider-holdout", + "key": "provider-model", + "label": "Provider model", + "kind": "env", + "required": false, + "next_action": "Optionally set YAO_OUTPUT_EVAL_MODEL, or pass --provider-model for the selected provider.", + "secret_value_redacted": false, + "env": "YAO_OUTPUT_EVAL_MODEL", + "status": "optional", + "actual": "not-set", + "default": "provider-specific model, or pass --provider-model" + } + ], + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/provider-holdout.json", + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 1, + "submission_ref_ready_count": 1, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + } + ] + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "status": "ready-for-human-review", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "prechecks": [ + { + "evidence_key": "human-adjudication", + "key": "review-kit", + "label": "Blind review kit", + "kind": "file", + "required": true, + "next_action": "Open the blind review kit and record real reviewer choices with required rationale.", + "secret_value_redacted": false, + "path": "reports/output_review_kit.html", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-template", + "label": "Decision template", + "kind": "file", + "required": true, + "next_action": "Import real A/B decisions with reviewer, reviewed_at, winner_variant, confidence, reason, and truthful blind-review attestation via `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication`.", + "secret_value_redacted": false, + "path": "reports/output_review_decisions.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-importer", + "label": "Decision importer", + "kind": "file", + "required": true, + "next_action": "Use the importer to reject raw content fields and normalize reviewer decisions before adjudication.", + "secret_value_redacted": false, + "path": "scripts/import_output_review_decisions.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "human-reviewer", + "label": "Human reviewer", + "kind": "human", + "required": true, + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "secret_value_redacted": false, + "status": "human-required", + "actual": "external-human-action" + } + ], + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/human-adjudication.json", + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 8, + "supporting_evidence_ready_count": 8, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ] + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "status": "blocked", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "prechecks": [ + { + "evidence_key": "native-permission-enforcement", + "key": "permission-policy", + "label": "Permission policy", + "kind": "file", + "required": true, + "next_action": "Keep approved high-permission capabilities explicit.", + "secret_value_redacted": false, + "path": "security/permission_policy.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "permission-probes", + "label": "Runtime probes", + "kind": "file", + "required": true, + "next_action": "Refresh runtime permission probes after packaging changes.", + "secret_value_redacted": false, + "path": "reports/runtime_permission_probes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "native-guard", + "label": "Native guard", + "kind": "external", + "required": true, + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + } + ], + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 11, + "supporting_evidence_ready_count": 11, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ] + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "prechecks": [ + { + "evidence_key": "native-client-telemetry", + "key": "native-host", + "label": "Native telemetry host", + "kind": "file", + "required": true, + "next_action": "Use the native host to receive metadata-only client events.", + "secret_value_redacted": false, + "path": "scripts/telemetry_native_host.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "hook-recipes", + "label": "Hook recipes", + "kind": "file", + "required": true, + "next_action": "Refresh telemetry hook recipes before external client installation.", + "secret_value_redacted": false, + "path": "reports/telemetry_hook_recipes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "external-client", + "label": "External client", + "kind": "external", + "required": true, + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + } + ], + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit": { + "prepare_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "prefill_command": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions --prefill-artifacts", + "output_dir": "evidence/world_class/submissions", + "draft_path": "evidence/world_class/submissions/native-client-telemetry.json", + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 2, + "submission_ref_ready_count": 2, + "supporting_evidence_total_count": 6, + "supporting_evidence_ready_count": 6, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + }, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false + }, + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "repair_checklist": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ] + } + ], + "prechecks": [ + { + "evidence_key": "provider-holdout", + "key": "output-cases", + "label": "Output eval cases", + "kind": "file", + "required": true, + "next_action": "Keep output holdout cases available before provider execution.", + "secret_value_redacted": false, + "path": "evals/output/cases.jsonl", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-runner", + "label": "Provider runner", + "kind": "file", + "required": true, + "next_action": "Use the provider runner instead of the local command runner for model-backed evidence.", + "secret_value_redacted": false, + "path": "scripts/provider_output_eval_runner.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "provider-holdout", + "key": "provider-api-key", + "label": "Provider credential", + "kind": "env_any", + "required": true, + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "secret_value_redacted": true, + "env_any": [ + "OPENAI_API_KEY", + "DEEPSEEK_API_KEY" + ], + "status": "missing", + "actual": "not-set", + "set_count": 0, + "set_envs": [] + }, + { + "evidence_key": "provider-holdout", + "key": "provider-model", + "label": "Provider model", + "kind": "env", + "required": false, + "next_action": "Optionally set YAO_OUTPUT_EVAL_MODEL, or pass --provider-model for the selected provider.", + "secret_value_redacted": false, + "env": "YAO_OUTPUT_EVAL_MODEL", + "status": "optional", + "actual": "not-set", + "default": "provider-specific model, or pass --provider-model" + }, + { + "evidence_key": "human-adjudication", + "key": "review-kit", + "label": "Blind review kit", + "kind": "file", + "required": true, + "next_action": "Open the blind review kit and record real reviewer choices with required rationale.", + "secret_value_redacted": false, + "path": "reports/output_review_kit.html", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-template", + "label": "Decision template", + "kind": "file", + "required": true, + "next_action": "Import real A/B decisions with reviewer, reviewed_at, winner_variant, confidence, reason, and truthful blind-review attestation via `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication`.", + "secret_value_redacted": false, + "path": "reports/output_review_decisions.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "decision-importer", + "label": "Decision importer", + "kind": "file", + "required": true, + "next_action": "Use the importer to reject raw content fields and normalize reviewer decisions before adjudication.", + "secret_value_redacted": false, + "path": "scripts/import_output_review_decisions.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "human-adjudication", + "key": "human-reviewer", + "label": "Human reviewer", + "kind": "human", + "required": true, + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "secret_value_redacted": false, + "status": "human-required", + "actual": "external-human-action" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "permission-policy", + "label": "Permission policy", + "kind": "file", + "required": true, + "next_action": "Keep approved high-permission capabilities explicit.", + "secret_value_redacted": false, + "path": "security/permission_policy.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "permission-probes", + "label": "Runtime probes", + "kind": "file", + "required": true, + "next_action": "Refresh runtime permission probes after packaging changes.", + "secret_value_redacted": false, + "path": "reports/runtime_permission_probes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-permission-enforcement", + "key": "native-guard", + "label": "Native guard", + "kind": "external", + "required": true, + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + }, + { + "evidence_key": "native-client-telemetry", + "key": "native-host", + "label": "Native telemetry host", + "kind": "file", + "required": true, + "next_action": "Use the native host to receive metadata-only client events.", + "secret_value_redacted": false, + "path": "scripts/telemetry_native_host.py", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "hook-recipes", + "label": "Hook recipes", + "kind": "file", + "required": true, + "next_action": "Refresh telemetry hook recipes before external client installation.", + "secret_value_redacted": false, + "path": "reports/telemetry_hook_recipes.json", + "status": "pass", + "actual": "present" + }, + { + "evidence_key": "native-client-telemetry", + "key": "external-client", + "label": "External client", + "kind": "external", + "required": true, + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "secret_value_redacted": false, + "status": "external-required", + "actual": "external-integration-required" + } + ], + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + }, + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + }, + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "submissions": { + "directory": "evidence/world_class/submissions", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions", + "prepare_prefilled_submission": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "submission_review": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "submission_kit_command": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions", + "submission_kit_prefill_command": "python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "preflight_counts_submission_as_completion": false, + "drafts_count_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "artifact_role_contract": { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": false, + "artifact_prefill_counts_as_evidence": false, + "submission_ref_total_count": 7, + "submission_ref_ready_count": 7, + "supporting_evidence_total_count": 31, + "supporting_evidence_ready_count": 31, + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": true, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs." + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": false, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs." + } + ] + } + }, + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json", + "submission_review": "reports/world_class_submission_review.json", + "operator_runbook": "reports/world_class_operator_runbook.json" + }, + "artifacts": { + "json": "reports/world_class_evidence_preflight.json", + "markdown": "reports/world_class_evidence_preflight.md", + "html": "reports/world_class_evidence_preflight.html" + } +} diff --git a/reports/world_class_evidence_preflight.md b/reports/world_class_evidence_preflight.md new file mode 100644 index 0000000..ac6f5bd --- /dev/null +++ b/reports/world_class_evidence_preflight.md @@ -0,0 +1,200 @@ +# World-Class Evidence Preflight + +Generated at: `2026-07-02` + +## Summary + +- decision: `collection-preflight-blocked` +- ready to claim world-class: `false` +- preflight counts as evidence: `false` +- credential value exposed: `false` +- collection ready: `1` +- collection blocked: `3` +- source checks: `12` pass / `19` total +- repair rows: `11` blocked / `11` total +- phase queue: `2` blocked / `2` phases +- phase queue rows: `11` +- next repair action: `human-adjudication-precheck-human-reviewer` +- next repair owner: `human reviewer` +- next phase: `unblock-access` +- next phase action: `human-adjudication-precheck-human-reviewer` + +This preflight report checks whether an operator can start collecting the remaining external or human evidence. It never accepts evidence, prints secret values, or changes the world-class ledger. + +## Submission Kit Handoff + +- submissions directory: `evidence/world_class/submissions` +- prepare drafts: `python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions` +- prepare drafts with artifact SHA prefill: `python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts` +- validate intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- review queue: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard claims: `python3 scripts/yao.py world-class-claim-guard .` +- drafts count as evidence: `false` +- artifact prefill counts as evidence: `false` +- submission refs ready: `7` / `7` +- supporting evidence ready: `31` / `31` + +Generate the submission kit after the real provider, human, native-permission, or native-client work exists. The generated JSON drafts remain `template_only: true` until an operator edits them with real aggregate artifact references and matching SHA-256 digests. The prefill command only inserts local artifact SHA-256 digests; it does not make a draft count as evidence. + +| Role | Copy to artifact_refs | Ready | Meaning | +| --- | --- | --- | --- | +| `submission-ref` | `true` | `7 / 7` | Rows marked submission-ref are the aggregate paths expected in artifact_refs. | +| `supporting-evidence` | `false` | `31 / 31` | Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs. | + +`submission-ref` rows are the only checklist rows expected in `artifact_refs`; `supporting-evidence` rows stay available for audit context and reviewer traceability. + +## Phase Queue + +Phase queue rows group the same repair checklist into operator execution phases. They are procedural guidance only and do not count as completion evidence. + +| Priority | Phase | Status | Rows | Owners | Evidence | Verify | Next action | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `20` | `unblock-access` | `blocked` | 4 / 4 blocked | Browser/Chrome/IDE/provider client integrator, human reviewer, operator with provider credentials, target client or installer integrator | human-adjudication, native-client-telemetry, native-permission-enforcement, provider-holdout | `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Assign a real reviewer identity before claiming human adjudication. | +| `40` | `collect-source` | `blocked` | 7 / 7 blocked | Browser/Chrome/IDE/provider client integrator, human reviewer, target client or installer integrator | human-adjudication, native-client-telemetry, native-permission-enforcement | `python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Set reviewer_attestation only after choices are completed before opening the answer key. | + +## Evidence Items + +| Evidence | Status | Intake | Review | Next action | +| --- | --- | --- | --- | --- | +| `provider-holdout` | `blocked` | `awaiting-submission` | `awaiting-submission` | Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. | +| `human-adjudication` | `ready-for-human-review` | `awaiting-submission` | `awaiting-submission` | Assign a real reviewer identity before claiming human adjudication. | +| `native-permission-enforcement` | `blocked` | `awaiting-submission` | `awaiting-submission` | Attach a real target-client or external installer runtime guard; metadata fallback is not enough. | +| `native-client-telemetry` | `blocked` | `awaiting-submission` | `awaiting-submission` | Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events. | + +## Repair Checklist + +Repair rows convert preflight and source blockers into a prioritized operator queue. They are guidance only and do not count as completion evidence. + +| Priority | Phase | Owner | Evidence | Type | Target | Status | Verify | Next action | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| `20` | `unblock-access` | human reviewer | `human-adjudication` | `precheck` | `human-reviewer` | `blocked` | `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Assign a real reviewer identity before claiming human adjudication. | +| `20` | `unblock-access` | Browser/Chrome/IDE/provider client integrator | `native-client-telemetry` | `precheck` | `external-client` | `blocked` | `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events. | +| `20` | `unblock-access` | target client or installer integrator | `native-permission-enforcement` | `precheck` | `native-guard` | `blocked` | `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Attach a real target-client or external installer runtime guard; metadata fallback is not enough. | +| `20` | `unblock-access` | operator with provider credentials | `provider-holdout` | `precheck` | `provider-api-key` | `blocked` | `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. | +| `40` | `collect-source` | human reviewer | `human-adjudication` | `source-check` | `blind_review_attested` | `blocked` | `python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Set reviewer_attestation only after choices are completed before opening the answer key. | +| `40` | `collect-source` | human reviewer | `human-adjudication` | `source-check` | `judgment_count` | `blocked` | `python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Every pair needs one valid human judgment. | +| `40` | `collect-source` | human reviewer | `human-adjudication` | `source-check` | `pending_count` | `blocked` | `python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Record a reviewer choice and reason for every pair. | +| `40` | `collect-source` | human reviewer | `human-adjudication` | `source-check` | `ready_for_human_evidence` | `blocked` | `python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints. | +| `40` | `collect-source` | human reviewer | `human-adjudication` | `source-check` | `reviewer_metadata_present` | `blocked` | `python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Record reviewer and reviewed_at before adjudication can count. | +| `40` | `collect-source` | Browser/Chrome/IDE/provider client integrator | `native-client-telemetry` | `source-check` | `external_source_events` | `blocked` | `python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Import at least one metadata-only event from a real client. | +| `40` | `collect-source` | target client or installer integrator | `native-permission-enforcement` | `source-check` | `native_enforcement_count` | `blocked` | `python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | Collect real target-client or external runtime guard proof. | + +## Provider Holdout + +- status: `blocked` +- ledger: `pending` +- submission: `evidence/world_class/submissions/provider-holdout.json` +- prepare draft: `python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions` +- prepare draft with artifact SHA prefill: `python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts` +- submission refs ready: `1` / `1` +- supporting evidence ready: `6` / `6` + +### Prechecks + +| Check | Kind | Current | Status | Next action | +| --- | --- | --- | --- | --- | +| Output eval cases | `file` | `present` | `pass` | Keep output holdout cases available before provider execution. | +| Provider runner | `file` | `present` | `pass` | Use the provider runner instead of the local command runner for model-backed evidence. | +| Provider credential | `env_any` | `not-set` | `missing` | Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. | +| Provider model | `env` | `not-set` | `optional` | Optionally set YAO_OUTPUT_EVAL_MODEL, or pass --provider-model for the selected provider. | + +### Source Checks + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| Provider model run | `10` | `>0` | `pass` | Run provider-backed output-exec with real credentials. | +| Timing observed | `10` | `>0` | `pass` | Provider execution should record timing metadata. | +| Token usage observed | `10` | `>0` | `pass` | Provider execution should return non-estimated token usage. | + +## Human Adjudication + +- status: `ready-for-human-review` +- ledger: `pending` +- submission: `evidence/world_class/submissions/human-adjudication.json` +- prepare draft: `python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions` +- prepare draft with artifact SHA prefill: `python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions --prefill-artifacts` +- submission refs ready: `2` / `2` +- supporting evidence ready: `8` / `8` + +### Prechecks + +| Check | Kind | Current | Status | Next action | +| --- | --- | --- | --- | --- | +| Blind review kit | `file` | `present` | `pass` | Open the blind review kit and record real reviewer choices with required rationale. | +| Decision template | `file` | `present` | `pass` | Import real A/B decisions with reviewer, reviewed_at, winner_variant, confidence, reason, and truthful blind-review attestation via `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication`. | +| Decision importer | `file` | `present` | `pass` | Use the importer to reject raw content fields and normalize reviewer decisions before adjudication. | +| Human reviewer | `human` | `external-human-action` | `human-required` | Assign a real reviewer identity before claiming human adjudication. | + +### Source Checks + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| Review pairs exist | `5` | `>0` | `pass` | Generate the blind A/B review pack. | +| No pending decisions | `5` | `==0` | `blocked` | Record a reviewer choice and reason for every pair. | +| Judgments complete | `0` | `==pair_count` | `blocked` | Every pair needs one valid human judgment. | +| No invalid decisions | `0` | `==0` | `pass` | Fix malformed winner/confidence entries. | +| Reviewer metadata | `False` | `true` | `blocked` | Record reviewer and reviewed_at before adjudication can count. | +| Reason required | `True` | `true` | `pass` | Keep reason mandatory for every imported or direct reviewer decision. | +| Blind review attested | `False` | `true` | `blocked` | Set reviewer_attestation only after choices are completed before opening the answer key. | +| Raw content attested | `True` | `true` | `pass` | Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content. | +| Raw content blocked | `False` | `false` | `pass` | Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages. | +| Human evidence ready | `False` | `true` | `blocked` | Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints. | + +## Native Permission Enforcement + +- status: `blocked` +- ledger: `pending` +- submission: `evidence/world_class/submissions/native-permission-enforcement.json` +- prepare draft: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions` +- prepare draft with artifact SHA prefill: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions --prefill-artifacts` +- submission refs ready: `2` / `2` +- supporting evidence ready: `11` / `11` + +### Prechecks + +| Check | Kind | Current | Status | Next action | +| --- | --- | --- | --- | --- | +| Permission policy | `file` | `present` | `pass` | Keep approved high-permission capabilities explicit. | +| Runtime probes | `file` | `present` | `pass` | Refresh runtime permission probes after packaging changes. | +| Native guard | `external` | `external-integration-required` | `external-required` | Attach a real target-client or external installer runtime guard; metadata fallback is not enough. | + +### Source Checks + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| Native enforcement | `0` | `>0` | `blocked` | Collect real target-client or external runtime guard proof. | +| Probe failures | `0` | `==0` | `pass` | Runtime permission probes must stay clean. | +| Installer support | `True` | `true` | `pass` | Installer enforcement is supporting evidence, not native proof. | + +## Native Client Telemetry + +- status: `blocked` +- ledger: `pending` +- submission: `evidence/world_class/submissions/native-client-telemetry.json` +- prepare draft: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions` +- prepare draft with artifact SHA prefill: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions --prefill-artifacts` +- submission refs ready: `2` / `2` +- supporting evidence ready: `6` / `6` + +### Prechecks + +| Check | Kind | Current | Status | Next action | +| --- | --- | --- | --- | --- | +| Native telemetry host | `file` | `present` | `pass` | Use the native host to receive metadata-only client events. | +| Hook recipes | `file` | `present` | `pass` | Refresh telemetry hook recipes before external client installation. | +| External client | `external` | `external-integration-required` | `external-required` | Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events. | + +### Source Checks + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| External events | `0` | `>0` | `blocked` | Import at least one metadata-only event from a real client. | +| Adoption sample | `1` | `>0` | `pass` | Telemetry must include adoption outcome evidence. | +| Raw content blocked | `False` | `false` | `pass` | Telemetry must stay metadata-only. | + +## Boundary + +- Environment variables are reported only as `set` or `not-set`; values are never printed. +- Human-required and external-required states are operator actions, not accepted evidence. +- The world-class ledger remains the source of truth for `ready_to_claim_world_class`. diff --git a/reports/world_class_operator_runbook.html b/reports/world_class_operator_runbook.html new file mode 100644 index 0000000..6865cc1 --- /dev/null +++ b/reports/world_class_operator_runbook.html @@ -0,0 +1,168 @@ + + + + + + World-Class Operator Runbook + + + + +
    +
    + Evidence Operations +

    World-Class Operator Runbook

    +

    A single operating page for collecting the remaining human and external evidence. It coordinates action, but does not accept evidence or change the ledger.

    +
    Pending4
    Awaiting4
    Ready0
    Source12/19
    Queue2/2
    Steps6/6
    Gate4/5
    Blocked7
    Invalid0
    +
    +

    Fast Path

    1. Run the real external or human work for one evidence item.
    2. Generate and fill the matching submission draft.
    3. Validate intake and inspect the submission review queue.
    4. Refresh the ledger and run the claim guard before making any completion claim.
    +

    Coordination Plan

    These steps tell the assistant what to run and where the user must supply real provider, reviewer, client, or telemetry input. Every row is operational guidance only and keeps counts_as_completion false.

    StepEvidenceOwnerUserUser actionAssistant actionCommandPass condition
    prepare-evidence-sessionallassistant + usertrueConfirm provider access, reviewer availability, target client path, and telemetry client path before collection starts.Run preflight and prepare submission drafts without accepting them as evidence.python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifactsPreflight lists the same pending evidence keys and no credential values are printed.
    collect-provider-holdoutprovider-holdoutassistant + operator with provider credentialstrueProvide the selected provider API key through an environment variable and confirm the provider, model, endpoint, and API format to use.Run provider-backed output execution, verify aggregate timing and token metadata, then prepare the evidence packet.python3 scripts/yao.py output-exec . --provider-runner <openai|deepseek> --provider-model <model> --timeout-seconds 60reports/output_execution_runs.json has model_executed_count > 0 and token_observed_count > 0.
    collect-human-adjudicationhuman-adjudicationhuman reviewer + assistanttrueOpen the blind review kit, choose winners for all pairs, add reviewer metadata and reasons, and keep the answer key hidden until decisions are saved.Generate the review kit, import decisions, validate integrity, and prepare the human evidence packet.python3 scripts/yao.py output-review-kit . && python3 scripts/yao.py output-review .reports/output_review_adjudication.json has pending_count == 0 and ready_for_human_evidence == true.
    collect-native-permission-enforcementnative-permission-enforcementtarget client or installer integrator + assistanttrueSelect a real target client or external installer guard that can enforce declared capabilities instead of metadata-only fallback.Run runtime permission probes, package verification, install simulation, and prepare the native enforcement evidence packet.python3 scripts/yao.py runtime-permissions . --package-dir distreports/runtime_permission_probes.json has native_enforcement_count > 0 and failure_count == 0.
    collect-native-client-telemetrynative-client-telemetryreal client integrator + assistanttrueInstall the native host manifest in a real Browser, Chrome, IDE, or provider client and trigger a metadata-only event.Generate native host assets, import the external event JSONL, refresh adoption drift, and prepare the telemetry evidence packet.python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl --source externalreports/adoption_drift_report.json has source_types.external > 0 and adoption_sample_count > 0.
    review-and-release-gateallassistant + ledger reviewertrueApprove only validated evidence packets and confirm the release wording after the claim guard passes.Run intake, submission review, ledger, claim guard, benchmark, evidence consistency, Review Studio, and CI before final publish.python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard . && make ci-testLedger ready_to_claim_world_class, benchmark public_claim_ready, claim guard violation_count == 0, Review Studio has no blockers, and CI passes.
    +

    Phase Queue

    • Unblock accessunblock-access: 4 / 4Assign a real reviewer identity before claiming human adjudication.
    • Collect sourcecollect-source: 7 / 7Set reviewer_attestation only after choices are completed before opening the answer key.
    +

    Evidence Items

    +
    external · awaiting-submission

    Provider Holdout

    +

    No evidence packet has been submitted for review.

    +
    +
    Owner
    operator with provider credentials
    +
    Ledger
    pending
    +
    Blocked
    0
    +
    Queue
    1
    +
    Submission
    evidence/world_class/submissions/provider-holdout.json
    +
    +

    Phase Queue

    • Unblock accessunblock-access: 1 / 1Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    +

    Source Runbook

    • Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.
    • For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60
    • For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +

    Commands

    • prepare submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions
    • validate intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • review queuepython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard claimpython3 scripts/yao.py world-class-claim-guard .
    +
    +

    Must Collect

    • provider-backed model run
    • observed timing
    • observed token metadata
    +

    Success Checks

    • reports/output_execution_runs.json summary.model_executed_count > 0
    • reports/output_execution_runs.json summary.timing_observed_count > 0
    • reports/output_execution_runs.json summary.token_observed_count > 0
    • reports/skill_os2_audit.json item provider-holdout status becomes pass
    +

    Privacy

    • Do not commit provider credentials or environment dumps.
    • The output execution report records output hashes and aggregate run metadata, not raw provider prompts.
    +
    +

    Next Source Actions

    • No blocked source checks.
    +

    Source Evidence Snapshot

    • Provider model runmodel_executed_count: 10 / >0Run provider-backed output-exec with real credentials.
    • Timing observedtiming_observed_count: 10 / >0Provider execution should record timing metadata.
    • Token usage observedtoken_observed_count: 10 / >0Provider execution should return non-estimated token usage.
    +
    +
    human · awaiting-submission

    Human Adjudication

    +

    No evidence packet has been submitted for review.

    +
    +
    Owner
    human reviewer
    +
    Ledger
    pending
    +
    Blocked
    5
    +
    Queue
    2
    +
    Submission
    evidence/world_class/submissions/human-adjudication.json
    +
    +

    Phase Queue

    • Unblock accessunblock-access: 1 / 1Assign a real reviewer identity before claiming human adjudication.
    • Collect sourcecollect-source: 5 / 5Set reviewer_attestation only after choices are completed before opening the answer key.
    +

    Source Runbook

    • python3 scripts/yao.py output-review-kit --write-template
    • Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.
    • python3 scripts/adjudicate_output_review.py --write-template
    • Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.
    • python3 scripts/yao.py output-review-import --input <reviewer-decisions.json> --blind-review-attested --run-adjudication
    • python3 scripts/yao.py output-review
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +

    Commands

    • prepare submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions
    • validate intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • review queuepython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard claimpython3 scripts/yao.py world-class-claim-guard .
    +
    +

    Must Collect

    • real reviewer identity
    • blind A/B decisions
    • answer key unopened until decisions exist
    +

    Success Checks

    • reports/output_review_adjudication.json summary.pending_count == 0
    • reports/output_review_adjudication.json summary.judgment_count == summary.pair_count
    • reports/output_review_adjudication.json summary.invalid_decision_count == 0
    • reports/output_review_adjudication.json summary.reviewer_metadata_present is true
    • reports/output_review_adjudication.json summary.blind_review_attested is true
    • reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json
    • reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text
    • reports/output_review_adjudication.json summary.ready_for_human_evidence is true
    • reports/skill_os2_audit.json item human-adjudication status becomes pass
    +

    Privacy

    • Reviewer decisions should not include raw user data or private customer detail.
    • Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.
    • The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.
    • The adjudication evidence stores prompt_sha256 instead of raw prompt text.
    • The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.
    • Keep the answer key separate until after decisions are recorded.
    +
    +

    Next Source Actions

    • Record a reviewer choice and reason for every pair.
    • Every pair needs one valid human judgment.
    • Record reviewer and reviewed_at before adjudication can count.
    • Set reviewer_attestation only after choices are completed before opening the answer key.
    • Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.
    +

    Source Evidence Snapshot

    • Review pairs existpair_count: 5 / >0Generate the blind A/B review pack.
    • No pending decisionspending_count: 5 / ==0Record a reviewer choice and reason for every pair.
    • Judgments completejudgment_count: 0 / ==pair_countEvery pair needs one valid human judgment.
    • No invalid decisionsinvalid_decision_count: 0 / ==0Fix malformed winner/confidence entries.
    • Reviewer metadatareviewer_metadata_present: False / trueRecord reviewer and reviewed_at before adjudication can count.
    • Reason requiredreason_required: True / trueKeep reason mandatory for every imported or direct reviewer decision.
    • Blind review attestedblind_review_attested: False / trueSet reviewer_attestation only after choices are completed before opening the answer key.
    • Raw content attestedraw_content_excluded_attested: True / trueAttest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content.
    • Raw content blockedraw_content_allowed: False / falseAdjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages.
    • Human evidence readyready_for_human_evidence: False / trueComplete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.
    +
    +
    external · awaiting-submission

    Native Permission Enforcement

    +

    No evidence packet has been submitted for review.

    +
    +
    Owner
    target client or installer integrator
    +
    Ledger
    pending
    +
    Blocked
    1
    +
    Queue
    2
    +
    Submission
    evidence/world_class/submissions/native-permission-enforcement.json
    +
    +

    Phase Queue

    • Unblock accessunblock-access: 1 / 1Attach a real target-client or external installer runtime guard; metadata fallback is not enough.
    • Collect sourcecollect-source: 1 / 1Collect real target-client or external runtime guard proof.
    +

    Source Runbook

    • Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.
    • Update the generated target adapter only when the guard is actually enforced by that target.
    • python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip
    • python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation
    • python3 scripts/yao.py runtime-permissions . --package-dir dist
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +

    Commands

    • prepare submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions
    • validate intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • review queuepython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard claimpython3 scripts/yao.py world-class-claim-guard .
    +
    +

    Must Collect

    • real target client or external installer runtime guard
    • native enforcement flag or externally accepted guard proof
    • residual risk retained for fallback targets
    +

    Success Checks

    • reports/runtime_permission_probes.json summary.native_enforcement_count > 0
    • reports/runtime_permission_probes.json summary.failure_count == 0
    • reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence
    • reports/skill_os2_audit.json item native-permission-enforcement status becomes pass
    +

    Privacy

    • Do not mark native_enforcement true for metadata-only fallbacks.
    • Keep residual risks visible for targets that still rely on operator enforcement.
    +
    +

    Next Source Actions

    • Collect real target-client or external runtime guard proof.
    +

    Source Evidence Snapshot

    • Native enforcementnative_enforcement_count: 0 / >0Collect real target-client or external runtime guard proof.
    • Probe failuresfailure_count: 0 / ==0Runtime permission probes must stay clean.
    • Installer supportinstaller_enforcement_ready: True / trueInstaller enforcement is supporting evidence, not native proof.
    +
    +
    external · awaiting-submission

    Native Client Telemetry

    +

    No evidence packet has been submitted for review.

    +
    +
    Owner
    Browser/Chrome/IDE/provider client integrator
    +
    Ledger
    pending
    +
    Blocked
    1
    +
    Queue
    2
    +
    Submission
    evidence/world_class/submissions/native-client-telemetry.json
    +
    +

    Phase Queue

    • Unblock accessunblock-access: 1 / 1Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.
    • Collect sourcecollect-source: 1 / 1Import at least one metadata-only event from a real client.
    +

    Source Runbook

    • python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension://<extension-id>/
    • Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.
    • python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl
    • python3 scripts/yao.py skill-atlas --workspace-root .
    • python3 scripts/yao.py skill-os2-audit . --generated-at <YYYY-MM-DD>
    • Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.
    • python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    +

    Commands

    • prepare submissionpython3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions
    • validate intakepython3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions
    • review queuepython3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions
    • refresh ledgerpython3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions
    • guard claimpython3 scripts/yao.py world-class-claim-guard .
    +
    +

    Must Collect

    • real external client source
    • metadata-only event
    • local-first import path
    +

    Success Checks

    • reports/adoption_drift_report.json summary.source_types.external > 0
    • reports/adoption_drift_report.json summary.adoption_sample_count > 0
    • reports/skill_os2_audit.json item native-client-telemetry status becomes pass
    +

    Privacy

    • Telemetry must remain metadata-only and local-first.
    • Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field.
    +
    +

    Next Source Actions

    • Import at least one metadata-only event from a real client.
    +

    Source Evidence Snapshot

    • External eventsexternal_source_events: 0 / >0Import at least one metadata-only event from a real client.
    • Adoption sampleadoption_sample_count: 1 / >0Telemetry must include adoption outcome evidence.
    • Raw content blockedraw_content_allowed: False / falseTelemetry must stay metadata-only.
    +
    +

    Release Gate

    +
    Decisionblocked-until-evidence-accepted
    +
    Readyfalse
    +
    Blocked4/5
    +
    Countsfalse
    +
    +

    Run make ci-test in a clean worktree and verify GitHub Actions before converting the PR out of Draft.

    +
    CheckCurrentExpectedStatusArtifact
    World-class ledger readyevidence-pendingready_to_claim_world_class == trueblockedreports/world_class_evidence_ledger.json
    Claim guard cleanviolations 0; ledger ready Falseviolation_count == 0 and ledger_ready_to_claim_world_class == trueblockedreports/world_class_claim_guard.json
    Benchmark public claim readypublic_claim_ready Falsepublic_claim_ready == trueblockedreports/benchmark_reproducibility.json
    Review Studio cleanblockers 0; warnings 3blocker_count == 0 and warning_count == 0blockedreports/review-studio.json
    Evidence consistency cleanconsistentdecision == consistent and fail_count == 0passreports/evidence_consistency.json
    +

    Boundary

    • Planned work, draft packets, metadata fallback, pending human decisions, and local command runners do not count as completion.
    • Valid intake means ready for submission review; ledger review still requires passing source evidence.
    • The world-class ledger and claim guard remain the source of truth.
    +
    + + diff --git a/reports/world_class_operator_runbook.json b/reports/world_class_operator_runbook.json new file mode 100644 index 0000000..2e7da17 --- /dev/null +++ b/reports/world_class_operator_runbook.json @@ -0,0 +1,1691 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "summary": { + "evidence_item_count": 4, + "pending_count": 4, + "accepted_count": 0, + "awaiting_submission_count": 4, + "ready_for_ledger_review_count": 0, + "valid_packet_source_incomplete_count": 0, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "repair_checklist_count": 11, + "repair_blocked_count": 11, + "phase_queue_count": 2, + "phase_queue_blocked_count": 2, + "phase_queue_row_count": 11, + "phase_queue_next_phase": "unblock-access", + "phase_queue_next_action_id": "human-adjudication-precheck-human-reviewer", + "phase_queue_next_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "phase_queue_counts_as_completion": false, + "coordination_step_count": 6, + "coordination_user_required_step_count": 6, + "coordination_pending_evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "coordination_counts_as_completion": false, + "release_gate_ready": false, + "release_gate_blocked_count": 4, + "release_gate_check_count": 5, + "release_gate_counts_as_completion": false, + "ready_to_claim_world_class": false, + "runbook_counts_as_completion": false, + "decision": "collect-evidence" + }, + "submissions": { + "directory": "evidence/world_class/submissions", + "runbook_counts_submission_as_completion": false + }, + "items": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": true, + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "current": "model-executed 10; token-observed 10", + "execution_runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "template_path": "evidence/world_class/templates/provider-holdout.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "provider-backed model run", + "observed timing", + "observed token metadata" + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ] + }, + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "blocked_source_check_count": 0, + "next_source_actions": [], + "repair_checklist": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ], + "repair_blocked_count": 1, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "operator with provider credentials" + ], + "evidence_keys": [ + "provider-holdout" + ], + "next_action_id": "provider-holdout-precheck-provider-api-key", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 1, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/provider-holdout.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "current": "0/5 decisions; pending 5", + "execution_runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "template_path": "evidence/world_class/templates/human-adjudication.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real reviewer identity", + "blind A/B decisions", + "answer key unopened until decisions exist" + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ] + }, + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "blocked_source_check_count": 5, + "next_source_actions": [ + "Record a reviewer choice and reason for every pair.", + "Every pair needs one valid human judgment.", + "Record reviewer and reviewed_at before adjudication can count.", + "Set reviewer_attestation only after choices are completed before opening the answer key.", + "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + ], + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "repair_blocked_count": 6, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 5, + "row_count": 5, + "owners": [ + "human reviewer" + ], + "evidence_keys": [ + "human-adjudication" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/human-adjudication.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "current": "native-enforced targets 0; installer-enforced targets 4", + "execution_runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "template_path": "evidence/world_class/templates/native-permission-enforcement.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real target client or external installer runtime guard", + "native enforcement flag or externally accepted guard proof", + "residual risk retained for fallback targets" + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ] + }, + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "blocked_source_check_count": 1, + "next_source_actions": [ + "Collect real target-client or external runtime guard proof." + ], + "repair_checklist": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "repair_blocked_count": 2, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-precheck-native-guard", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "target client or installer integrator" + ], + "evidence_keys": [ + "native-permission-enforcement" + ], + "next_action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-permission-enforcement.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "ledger_status": "pending", + "intake_readiness": "awaiting-submission", + "review_state": "awaiting-submission", + "source_accepted": false, + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "current": "external source events 0; adoption samples 1", + "execution_runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + "Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ], + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "template_path": "evidence/world_class/templates/native-client-telemetry.intake.json", + "commands": { + "prepare_submission": "python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions", + "validate_intake": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "review_queue": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "refresh_ledger": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard ." + }, + "must_collect": { + "provenance_requirements": [ + "real external client source", + "metadata-only event", + "local-first import path" + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ] + }, + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md" + ], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "blocked_source_check_count": 1, + "next_source_actions": [ + "Import at least one metadata-only event from a real client." + ], + "repair_checklist": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "repair_blocked_count": 2, + "repair_counts_as_completion": false, + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-precheck-external-client", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 1, + "row_count": 1, + "owners": [ + "Browser/Chrome/IDE/provider client integrator" + ], + "evidence_keys": [ + "native-client-telemetry" + ], + "next_action_id": "native-client-telemetry-source-check-external_source_events", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "phase_queue_blocked_count": 2, + "phase_queue_counts_as_completion": false, + "submission_state": { + "status": "missing", + "path": "evidence/world_class/submissions/native-client-telemetry.json", + "artifact_ref_count": 0, + "attested_real_evidence": false, + "privacy_contract_satisfied": false, + "ledger_reviewer_approved": false, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": false + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": false, + "metadata_fallback_counts_as_native_enforcement": false, + "pending_review_counts_as_human_decision": false, + "local_command_runner_counts_as_provider_model": false + } + } + ], + "coordination_plan": [ + { + "step_id": "prepare-evidence-session", + "phase": "prepare", + "evidence_key": "", + "owner": "assistant + user", + "requires_user_input": true, + "user_action": "Confirm provider access, reviewer availability, target client path, and telemetry client path before collection starts.", + "assistant_action": "Run preflight and prepare submission drafts without accepting them as evidence.", + "external_dependency": "User-selected provider, reviewer, and real client surfaces.", + "command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "pass_condition": "Preflight lists the same pending evidence keys and no credential values are printed.", + "artifact": "reports/world_class_evidence_preflight.json", + "privacy_boundary": "Drafts and preflight are planning artifacts only; they do not count as evidence.", + "counts_as_completion": false + }, + { + "step_id": "collect-provider-holdout", + "phase": "provider-holdout", + "evidence_key": "provider-holdout", + "owner": "assistant + operator with provider credentials", + "requires_user_input": true, + "user_action": "Provide the selected provider API key through an environment variable and confirm the provider, model, endpoint, and API format to use.", + "assistant_action": "Run provider-backed output execution, verify aggregate timing and token metadata, then prepare the evidence packet.", + "external_dependency": "Valid provider credentials and a live provider endpoint.", + "command": "python3 scripts/yao.py output-exec . --provider-runner --provider-model --timeout-seconds 60", + "pass_condition": "reports/output_execution_runs.json has model_executed_count > 0 and token_observed_count > 0.", + "artifact": "reports/output_execution_runs.json", + "privacy_boundary": "Commit aggregate metadata only; do not commit API keys, raw prompts, raw outputs, or provider request payloads.", + "counts_as_completion": false + }, + { + "step_id": "collect-human-adjudication", + "phase": "human-review", + "evidence_key": "human-adjudication", + "owner": "human reviewer + assistant", + "requires_user_input": true, + "user_action": "Open the blind review kit, choose winners for all pairs, add reviewer metadata and reasons, and keep the answer key hidden until decisions are saved.", + "assistant_action": "Generate the review kit, import decisions, validate integrity, and prepare the human evidence packet.", + "external_dependency": "A real human reviewer who can make blind A/B judgments.", + "command": "python3 scripts/yao.py output-review-kit . && python3 scripts/yao.py output-review .", + "pass_condition": "reports/output_review_adjudication.json has pending_count == 0 and ready_for_human_evidence == true.", + "artifact": "reports/output_review_adjudication.json", + "privacy_boundary": "Store reviewer choices, reasons, hashes, and attestations; do not store raw prompts or answer-key exposure before decisions.", + "counts_as_completion": false + }, + { + "step_id": "collect-native-permission-enforcement", + "phase": "native-permission", + "evidence_key": "native-permission-enforcement", + "owner": "target client or installer integrator + assistant", + "requires_user_input": true, + "user_action": "Select a real target client or external installer guard that can enforce declared capabilities instead of metadata-only fallback.", + "assistant_action": "Run runtime permission probes, package verification, install simulation, and prepare the native enforcement evidence packet.", + "external_dependency": "A real target runtime, extension, client, or installer guard that can block undeclared capabilities.", + "command": "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "pass_condition": "reports/runtime_permission_probes.json has native_enforcement_count > 0 and failure_count == 0.", + "artifact": "reports/runtime_permission_probes.json", + "privacy_boundary": "Do not mark metadata fallback as native enforcement; keep residual risks visible for fallback targets.", + "counts_as_completion": false + }, + { + "step_id": "collect-native-client-telemetry", + "phase": "native-telemetry", + "evidence_key": "native-client-telemetry", + "owner": "real client integrator + assistant", + "requires_user_input": true, + "user_action": "Install the native host manifest in a real Browser, Chrome, IDE, or provider client and trigger a metadata-only event.", + "assistant_action": "Generate native host assets, import the external event JSONL, refresh adoption drift, and prepare the telemetry evidence packet.", + "external_dependency": "A real client process that emits metadata-only telemetry through the native host.", + "command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl --source external", + "pass_condition": "reports/adoption_drift_report.json has source_types.external > 0 and adoption_sample_count > 0.", + "artifact": "reports/adoption_drift_report.json", + "privacy_boundary": "Telemetry must remain metadata-only; do not store raw prompts, outputs, transcripts, notes, or messages.", + "counts_as_completion": false + }, + { + "step_id": "review-and-release-gate", + "phase": "release-gate", + "evidence_key": "", + "owner": "assistant + ledger reviewer", + "requires_user_input": true, + "user_action": "Approve only validated evidence packets and confirm the release wording after the claim guard passes.", + "assistant_action": "Run intake, submission review, ledger, claim guard, benchmark, evidence consistency, Review Studio, and CI before final publish.", + "external_dependency": "Accepted evidence packets for every pending world-class key.", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard . && make ci-test", + "pass_condition": "Ledger ready_to_claim_world_class, benchmark public_claim_ready, claim guard violation_count == 0, Review Studio has no blockers, and CI passes.", + "artifact": "reports/world_class_evidence_ledger.json", + "privacy_boundary": "Release evidence may include aggregate reports and hashes only; raw content and credentials remain out of git.", + "counts_as_completion": false + } + ], + "release_gate": { + "ready": false, + "decision": "blocked-until-evidence-accepted", + "check_count": 5, + "blocked_count": 4, + "checks": [ + { + "key": "world_class_ledger_ready", + "label": "World-class ledger ready", + "passed": false, + "current": "evidence-pending", + "expected": "ready_to_claim_world_class == true", + "artifact": "reports/world_class_evidence_ledger.json" + }, + { + "key": "claim_guard_clean", + "label": "Claim guard clean", + "passed": false, + "current": "violations 0; ledger ready False", + "expected": "violation_count == 0 and ledger_ready_to_claim_world_class == true", + "artifact": "reports/world_class_claim_guard.json" + }, + { + "key": "benchmark_public_claim_ready", + "label": "Benchmark public claim ready", + "passed": false, + "current": "public_claim_ready False", + "expected": "public_claim_ready == true", + "artifact": "reports/benchmark_reproducibility.json" + }, + { + "key": "review_studio_clean", + "label": "Review Studio clean", + "passed": false, + "current": "blockers 0; warnings 3", + "expected": "blocker_count == 0 and warning_count == 0", + "artifact": "reports/review-studio.json" + }, + { + "key": "evidence_consistency_clean", + "label": "Evidence consistency clean", + "passed": true, + "current": "consistent", + "expected": "decision == consistent and fail_count == 0", + "artifact": "reports/evidence_consistency.json" + } + ], + "blocked_checks": [ + { + "key": "world_class_ledger_ready", + "label": "World-class ledger ready", + "passed": false, + "current": "evidence-pending", + "expected": "ready_to_claim_world_class == true", + "artifact": "reports/world_class_evidence_ledger.json" + }, + { + "key": "claim_guard_clean", + "label": "Claim guard clean", + "passed": false, + "current": "violations 0; ledger ready False", + "expected": "violation_count == 0 and ledger_ready_to_claim_world_class == true", + "artifact": "reports/world_class_claim_guard.json" + }, + { + "key": "benchmark_public_claim_ready", + "label": "Benchmark public claim ready", + "passed": false, + "current": "public_claim_ready False", + "expected": "public_claim_ready == true", + "artifact": "reports/benchmark_reproducibility.json" + }, + { + "key": "review_studio_clean", + "label": "Review Studio clean", + "passed": false, + "current": "blockers 0; warnings 3", + "expected": "blocker_count == 0 and warning_count == 0", + "artifact": "reports/review-studio.json" + } + ], + "final_manual_check": "Run make ci-test in a clean worktree and verify GitHub Actions before converting the PR out of Draft.", + "counts_as_completion": false + }, + "repair_checklist": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ], + "phase_queue": [ + { + "phase": "unblock-access", + "label": "Unblock access", + "priority": 20, + "status": "blocked", + "blocked_count": 4, + "row_count": 4, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "operator with provider credentials", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout" + ], + "next_action_id": "human-adjudication-precheck-human-reviewer", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-precheck-human-reviewer", + "evidence_key": "human-adjudication", + "repair_type": "precheck", + "target": "human-reviewer", + "phase": "unblock-access", + "priority": 20, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Required human precheck is human-required.", + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-precheck-external-client", + "evidence_key": "native-client-telemetry", + "repair_type": "precheck", + "target": "external-client", + "phase": "unblock-access", + "priority": 20, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-precheck-native-guard", + "evidence_key": "native-permission-enforcement", + "repair_type": "precheck", + "target": "native-guard", + "phase": "unblock-access", + "priority": 20, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Required external precheck is external-required.", + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + }, + { + "action_id": "provider-holdout-precheck-provider-api-key", + "evidence_key": "provider-holdout", + "repair_type": "precheck", + "target": "provider-api-key", + "phase": "unblock-access", + "priority": 20, + "owner": "operator with provider credentials", + "status": "blocked", + "blocking_reason": "Required env_any precheck is missing.", + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "verification_command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "prechecks", + "counts_as_completion": false + } + ] + }, + { + "phase": "collect-source", + "label": "Collect source", + "priority": 40, + "status": "blocked", + "blocked_count": 7, + "row_count": 7, + "owners": [ + "Browser/Chrome/IDE/provider client integrator", + "human reviewer", + "target client or installer integrator" + ], + "evidence_keys": [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement" + ], + "next_action_id": "human-adjudication-source-check-blind_review_attested", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "counts_as_completion": false, + "rows": [ + { + "action_id": "human-adjudication-source-check-blind_review_attested", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "blind_review_attested", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-judgment_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "judgment_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '==pair_count'.", + "next_action": "Every pair needs one valid human judgment.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-pending_count", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "pending_count", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value 5 does not satisfy '==0'.", + "next_action": "Record a reviewer choice and reason for every pair.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-ready_for_human_evidence", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "ready_for_human_evidence", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "human-adjudication-source-check-reviewer_metadata_present", + "evidence_key": "human-adjudication", + "repair_type": "source-check", + "target": "reviewer_metadata_present", + "phase": "collect-source", + "priority": 40, + "owner": "human reviewer", + "status": "blocked", + "blocking_reason": "Current value False does not satisfy 'true'.", + "next_action": "Record reviewer and reviewed_at before adjudication can count.", + "verification_command": "python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-client-telemetry-source-check-external_source_events", + "evidence_key": "native-client-telemetry", + "repair_type": "source-check", + "target": "external_source_events", + "phase": "collect-source", + "priority": 40, + "owner": "Browser/Chrome/IDE/provider client integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Import at least one metadata-only event from a real client.", + "verification_command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + }, + { + "action_id": "native-permission-enforcement-source-check-native_enforcement_count", + "evidence_key": "native-permission-enforcement", + "repair_type": "source-check", + "target": "native_enforcement_count", + "phase": "collect-source", + "priority": 40, + "owner": "target client or installer integrator", + "status": "blocked", + "blocking_reason": "Current value 0 does not satisfy '>0'.", + "next_action": "Collect real target-client or external runtime guard proof.", + "verification_command": "python3 scripts/yao.py runtime-permissions . --package-dir dist && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "source": "source_checklist", + "counts_as_completion": false + } + ] + } + ], + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json", + "preflight": "reports/world_class_evidence_preflight.json", + "submission_review": "reports/world_class_submission_review.json", + "claim_guard": "reports/world_class_claim_guard.json" + }, + "artifacts": { + "json": "reports/world_class_operator_runbook.json", + "markdown": "reports/world_class_operator_runbook.md", + "html": "reports/world_class_operator_runbook.html" + } +} diff --git a/reports/world_class_operator_runbook.md b/reports/world_class_operator_runbook.md new file mode 100644 index 0000000..a5270e1 --- /dev/null +++ b/reports/world_class_operator_runbook.md @@ -0,0 +1,407 @@ +# World-Class Operator Runbook + +Generated at: `2026-07-02` + +## Summary + +- decision: `collect-evidence` +- ready to claim world-class: `false` +- runbook counts as completion: `false` +- evidence items: `4` +- pending: `4` +- awaiting submission: `4` +- ready for ledger review: `0` +- phase queue: `2` blocked / `2` phases +- phase queue rows: `11` +- phase queue counts as completion: `false` +- coordination steps: `6` user-required / `6` total +- coordination pending keys: `human-adjudication, native-client-telemetry, native-permission-enforcement, provider-holdout` +- coordination counts as completion: `false` +- release gate ready: `false` +- release gate blocked checks: `4` / `5` +- release gate counts as completion: `false` + +This runbook coordinates evidence collection only. It does not accept submissions or make world-class completion true. + +## Fast Path + +1. Run the real external or human work for one evidence item. +2. Generate the matching submission draft. +3. Replace template-only fields with aggregate evidence and provenance. +4. Validate intake, review the queue, refresh the ledger, then run the claim guard. + +## Coordination Plan + +| Step | Evidence | Owner | Needs user | User action | Assistant action | Command | Pass condition | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `prepare-evidence-session` | `all` | assistant + user | `true` | Confirm provider access, reviewer availability, target client path, and telemetry client path before collection starts. | Run preflight and prepare submission drafts without accepting them as evidence. | `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts` | Preflight lists the same pending evidence keys and no credential values are printed. | +| `collect-provider-holdout` | `provider-holdout` | assistant + operator with provider credentials | `true` | Provide the selected provider API key through an environment variable and confirm the provider, model, endpoint, and API format to use. | Run provider-backed output execution, verify aggregate timing and token metadata, then prepare the evidence packet. | `python3 scripts/yao.py output-exec . --provider-runner --provider-model --timeout-seconds 60` | reports/output_execution_runs.json has model_executed_count > 0 and token_observed_count > 0. | +| `collect-human-adjudication` | `human-adjudication` | human reviewer + assistant | `true` | Open the blind review kit, choose winners for all pairs, add reviewer metadata and reasons, and keep the answer key hidden until decisions are saved. | Generate the review kit, import decisions, validate integrity, and prepare the human evidence packet. | `python3 scripts/yao.py output-review-kit . && python3 scripts/yao.py output-review .` | reports/output_review_adjudication.json has pending_count == 0 and ready_for_human_evidence == true. | +| `collect-native-permission-enforcement` | `native-permission-enforcement` | target client or installer integrator + assistant | `true` | Select a real target client or external installer guard that can enforce declared capabilities instead of metadata-only fallback. | Run runtime permission probes, package verification, install simulation, and prepare the native enforcement evidence packet. | `python3 scripts/yao.py runtime-permissions . --package-dir dist` | reports/runtime_permission_probes.json has native_enforcement_count > 0 and failure_count == 0. | +| `collect-native-client-telemetry` | `native-client-telemetry` | real client integrator + assistant | `true` | Install the native host manifest in a real Browser, Chrome, IDE, or provider client and trigger a metadata-only event. | Generate native host assets, import the external event JSONL, refresh adoption drift, and prepare the telemetry evidence packet. | `python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl --source external` | reports/adoption_drift_report.json has source_types.external > 0 and adoption_sample_count > 0. | +| `review-and-release-gate` | `all` | assistant + ledger reviewer | `true` | Approve only validated evidence packets and confirm the release wording after the claim guard passes. | Run intake, submission review, ledger, claim guard, benchmark, evidence consistency, Review Studio, and CI before final publish. | `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard . && make ci-test` | Ledger ready_to_claim_world_class, benchmark public_claim_ready, claim guard violation_count == 0, Review Studio has no blockers, and CI passes. | + +## Phase Queue + +| Phase | Status | Rows | Blocked | Owners | Next action | Verify | +| --- | --- | ---: | ---: | --- | --- | --- | +| `unblock-access` | `blocked` | `4` | `4` | Browser/Chrome/IDE/provider client integrator, human reviewer, operator with provider credentials, target client or installer integrator | Assign a real reviewer identity before claiming human adjudication. | `python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | +| `collect-source` | `blocked` | `7` | `7` | Browser/Chrome/IDE/provider client integrator, human reviewer, target client or installer integrator | Set reviewer_attestation only after choices are completed before opening the answer key. | `python3 scripts/yao.py output-review && python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions` | + +## Evidence Items + +| Evidence | Ledger | Intake | Review | Blocked checks | Next source action | Owner | +| --- | --- | --- | --- | ---: | --- | --- | +| `provider-holdout` | `pending` | `awaiting-submission` | `awaiting-submission` | `0` | none | operator with provider credentials | +| `human-adjudication` | `pending` | `awaiting-submission` | `awaiting-submission` | `5` | Record a reviewer choice and reason for every pair. | human reviewer | +| `native-permission-enforcement` | `pending` | `awaiting-submission` | `awaiting-submission` | `1` | Collect real target-client or external runtime guard proof. | target client or installer integrator | +| `native-client-telemetry` | `pending` | `awaiting-submission` | `awaiting-submission` | `1` | Import at least one metadata-only event from a real client. | Browser/Chrome/IDE/provider client integrator | + +## Provider Holdout + +- objective: Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata. +- blocking reason: No evidence packet has been submitted for review. +- blocked source checks: `0` +- repair rows: `1` blocked +- phase queue: `1` blocked phases +- submission: `evidence/world_class/submissions/provider-holdout.json` +- template: `evidence/world_class/templates/provider-holdout.intake.json` + +### Phase Queue + +| Phase | Status | Rows | Blocked | Next action | +| --- | --- | ---: | ---: | --- | +| `unblock-access` | `blocked` | `1` | `1` | Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. | + +### Source Runbook + +- Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value. +- For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60 +- For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120 +- python3 scripts/yao.py skill-os2-audit . --generated-at +- Copy evidence/world_class/templates/provider-holdout.intake.json to evidence/world_class/submissions/provider-holdout.json and fill only real evidence fields. +- python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions + +### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- review_queue: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +### Must Collect + +- provider-backed model run +- observed timing +- observed token metadata + +### Success Checks + +- reports/output_execution_runs.json summary.model_executed_count > 0 +- reports/output_execution_runs.json summary.timing_observed_count > 0 +- reports/output_execution_runs.json summary.token_observed_count > 0 +- reports/skill_os2_audit.json item provider-holdout status becomes pass + +### Privacy Contract + +- Do not commit provider credentials or environment dumps. +- The output execution report records output hashes and aggregate run metadata, not raw provider prompts. + +### Evidence Artifacts + +- reports/output_execution_runs.json +- reports/output_execution_runs.md +- reports/skill_os2_audit.json +- evidence/world_class/intake.schema.json +- evidence/world_class/templates/provider-holdout.intake.json +- reports/world_class_evidence_intake.json +- reports/world_class_evidence_intake.md + +### Next Source Actions + +- No blocked source checks. + +### Source Evidence Snapshot + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| Provider model run | `10` | `>0` | `pass` | Run provider-backed output-exec with real credentials. | +| Timing observed | `10` | `>0` | `pass` | Provider execution should record timing metadata. | +| Token usage observed | `10` | `>0` | `pass` | Provider execution should return non-estimated token usage. | + +## Human Adjudication + +- objective: Record real blind A/B reviewer decisions before claiming human output review completion. +- blocking reason: No evidence packet has been submitted for review. +- blocked source checks: `5` +- repair rows: `6` blocked +- phase queue: `2` blocked phases +- submission: `evidence/world_class/submissions/human-adjudication.json` +- template: `evidence/world_class/templates/human-adjudication.intake.json` + +### Phase Queue + +| Phase | Status | Rows | Blocked | Next action | +| --- | --- | ---: | ---: | --- | +| `unblock-access` | `blocked` | `1` | `1` | Assign a real reviewer identity before claiming human adjudication. | +| `collect-source` | `blocked` | `5` | `5` | Set reviewer_attestation only after choices are completed before opening the answer key. | + +### Source Runbook + +- python3 scripts/yao.py output-review-kit --write-template +- Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key. +- python3 scripts/adjudicate_output_review.py --write-template +- Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only. +- python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication +- python3 scripts/yao.py output-review +- python3 scripts/yao.py skill-os2-audit . --generated-at +- Copy evidence/world_class/templates/human-adjudication.intake.json to evidence/world_class/submissions/human-adjudication.json and fill only real evidence fields. +- python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions + +### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key human-adjudication --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- review_queue: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +### Must Collect + +- real reviewer identity +- blind A/B decisions +- answer key unopened until decisions exist + +### Success Checks + +- reports/output_review_adjudication.json summary.pending_count == 0 +- reports/output_review_adjudication.json summary.judgment_count == summary.pair_count +- reports/output_review_adjudication.json summary.invalid_decision_count == 0 +- reports/output_review_adjudication.json summary.reviewer_metadata_present is true +- reports/output_review_adjudication.json summary.blind_review_attested is true +- reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json +- reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text +- reports/output_review_adjudication.json summary.ready_for_human_evidence is true +- reports/skill_os2_audit.json item human-adjudication status becomes pass + +### Privacy Contract + +- Reviewer decisions should not include raw user data or private customer detail. +- Reviewer reasons must be rubric-based and must not include raw user data or private customer detail. +- The decision importer rejects raw prompt, output, transcript, message, and answer-key fields. +- The adjudication evidence stores prompt_sha256 instead of raw prompt text. +- The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged. +- Keep the answer key separate until after decisions are recorded. + +### Evidence Artifacts + +- reports/output_blind_review_pack.md +- reports/output_review_kit.md +- reports/output_review_decisions.json +- reports/output_review_adjudication.json +- reports/output_review_adjudication.md +- scripts/import_output_review_decisions.py +- evidence/world_class/intake.schema.json +- evidence/world_class/templates/human-adjudication.intake.json +- reports/world_class_evidence_intake.json +- reports/world_class_evidence_intake.md + +### Next Source Actions + +- Record a reviewer choice and reason for every pair. +- Every pair needs one valid human judgment. +- Record reviewer and reviewed_at before adjudication can count. +- Set reviewer_attestation only after choices are completed before opening the answer key. +- Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints. + +### Source Evidence Snapshot + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| Review pairs exist | `5` | `>0` | `pass` | Generate the blind A/B review pack. | +| No pending decisions | `5` | `==0` | `blocked` | Record a reviewer choice and reason for every pair. | +| Judgments complete | `0` | `==pair_count` | `blocked` | Every pair needs one valid human judgment. | +| No invalid decisions | `0` | `==0` | `pass` | Fix malformed winner/confidence entries. | +| Reviewer metadata | `False` | `true` | `blocked` | Record reviewer and reviewed_at before adjudication can count. | +| Reason required | `True` | `true` | `pass` | Keep reason mandatory for every imported or direct reviewer decision. | +| Blind review attested | `False` | `true` | `blocked` | Set reviewer_attestation only after choices are completed before opening the answer key. | +| Raw content attested | `True` | `true` | `pass` | Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content. | +| Raw content blocked | `False` | `false` | `pass` | Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages. | +| Human evidence ready | `False` | `true` | `blocked` | Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints. | + +## Native Permission Enforcement + +- objective: Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities. +- blocking reason: No evidence packet has been submitted for review. +- blocked source checks: `1` +- repair rows: `2` blocked +- phase queue: `2` blocked phases +- submission: `evidence/world_class/submissions/native-permission-enforcement.json` +- template: `evidence/world_class/templates/native-permission-enforcement.intake.json` + +### Phase Queue + +| Phase | Status | Rows | Blocked | Next action | +| --- | --- | ---: | ---: | --- | +| `unblock-access` | `blocked` | `1` | `1` | Attach a real target-client or external installer runtime guard; metadata fallback is not enough. | +| `collect-source` | `blocked` | `1` | `1` | Collect real target-client or external runtime guard proof. | + +### Source Runbook + +- Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities. +- Update the generated target adapter only when the guard is actually enforced by that target. +- python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip +- python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation +- python3 scripts/yao.py runtime-permissions . --package-dir dist +- python3 scripts/yao.py skill-os2-audit . --generated-at +- Copy evidence/world_class/templates/native-permission-enforcement.intake.json to evidence/world_class/submissions/native-permission-enforcement.json and fill only real evidence fields. +- python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions + +### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-permission-enforcement --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- review_queue: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +### Must Collect + +- real target client or external installer runtime guard +- native enforcement flag or externally accepted guard proof +- residual risk retained for fallback targets + +### Success Checks + +- reports/runtime_permission_probes.json summary.native_enforcement_count > 0 +- reports/runtime_permission_probes.json summary.failure_count == 0 +- reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence +- reports/skill_os2_audit.json item native-permission-enforcement status becomes pass + +### Privacy Contract + +- Do not mark native_enforcement true for metadata-only fallbacks. +- Keep residual risks visible for targets that still rely on operator enforcement. + +### Evidence Artifacts + +- dist/targets/*/adapter.json +- reports/runtime_permission_probes.json +- reports/runtime_permission_probes.md +- reports/install_simulation.json +- reports/install_simulation.md +- security/permission_policy.json +- evidence/world_class/intake.schema.json +- evidence/world_class/templates/native-permission-enforcement.intake.json +- reports/world_class_evidence_intake.json +- reports/world_class_evidence_intake.md + +### Next Source Actions + +- Collect real target-client or external runtime guard proof. + +### Source Evidence Snapshot + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| Native enforcement | `0` | `>0` | `blocked` | Collect real target-client or external runtime guard proof. | +| Probe failures | `0` | `==0` | `pass` | Runtime permission probes must stay clean. | +| Installer support | `True` | `true` | `pass` | Installer enforcement is supporting evidence, not native proof. | + +## Native Client Telemetry + +- objective: Import production metadata-only events from a real external client into the local drift loop. +- blocking reason: No evidence packet has been submitted for review. +- blocked source checks: `1` +- repair rows: `2` blocked +- phase queue: `2` blocked phases +- submission: `evidence/world_class/submissions/native-client-telemetry.json` +- template: `evidence/world_class/templates/native-client-telemetry.intake.json` + +### Phase Queue + +| Phase | Status | Rows | Blocked | Next action | +| --- | --- | ---: | ---: | --- | +| `unblock-access` | `blocked` | `1` | `1` | Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events. | +| `collect-source` | `blocked` | `1` | `1` | Import at least one metadata-only event from a real client. | + +### Source Runbook + +- python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:/// +- Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event. +- python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl +- python3 scripts/yao.py skill-atlas --workspace-root . +- python3 scripts/yao.py skill-os2-audit . --generated-at +- Copy evidence/world_class/templates/native-client-telemetry.intake.json to evidence/world_class/submissions/native-client-telemetry.json and fill only real evidence fields. +- python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions + +### Commands + +- prepare_submission: `python3 scripts/yao.py world-class-submission-kit . --evidence-key native-client-telemetry --output-dir evidence/world_class/submissions` +- validate_intake: `python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions` +- review_queue: `python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions` +- refresh_ledger: `python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions` +- guard_claim: `python3 scripts/yao.py world-class-claim-guard .` + +### Must Collect + +- real external client source +- metadata-only event +- local-first import path + +### Success Checks + +- reports/adoption_drift_report.json summary.source_types.external > 0 +- reports/adoption_drift_report.json summary.adoption_sample_count > 0 +- reports/skill_os2_audit.json item native-client-telemetry status becomes pass + +### Privacy Contract + +- Telemetry must remain metadata-only and local-first. +- Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field. + +### Evidence Artifacts + +- reports/adoption_drift_report.json +- reports/adoption_drift_report.md +- reports/telemetry_hook_recipes.json +- scripts/telemetry_native_host.py +- evidence/world_class/intake.schema.json +- evidence/world_class/templates/native-client-telemetry.intake.json +- reports/world_class_evidence_intake.json +- reports/world_class_evidence_intake.md + +### Next Source Actions + +- Import at least one metadata-only event from a real client. + +### Source Evidence Snapshot + +| Check | Current | Expected | Status | Next action | +| --- | --- | --- | --- | --- | +| External events | `0` | `>0` | `blocked` | Import at least one metadata-only event from a real client. | +| Adoption sample | `1` | `>0` | `pass` | Telemetry must include adoption outcome evidence. | +| Raw content blocked | `False` | `false` | `pass` | Telemetry must stay metadata-only. | + +## Release Gate + +- decision: `blocked-until-evidence-accepted` +- ready: `false` +- blocked checks: `4` / `5` +- counts as completion: `false` +- final manual check: Run make ci-test in a clean worktree and verify GitHub Actions before converting the PR out of Draft. + +| Check | Current | Expected | Status | Artifact | +| --- | --- | --- | --- | --- | +| World-class ledger ready | `evidence-pending` | `ready_to_claim_world_class == true` | `blocked` | `reports/world_class_evidence_ledger.json` | +| Claim guard clean | `violations 0; ledger ready False` | `violation_count == 0 and ledger_ready_to_claim_world_class == true` | `blocked` | `reports/world_class_claim_guard.json` | +| Benchmark public claim ready | `public_claim_ready False` | `public_claim_ready == true` | `blocked` | `reports/benchmark_reproducibility.json` | +| Review Studio clean | `blockers 0; warnings 3` | `blocker_count == 0 and warning_count == 0` | `blocked` | `reports/review-studio.json` | +| Evidence consistency clean | `consistent` | `decision == consistent and fail_count == 0` | `pass` | `reports/evidence_consistency.json` | + +## Boundary + +- Planned work, draft packets, metadata fallback, pending human decisions, and local command runners do not count as completion. +- Valid intake means ready for submission review; ledger review still requires passing source evidence. +- The world-class ledger and claim guard remain the source of truth. diff --git a/reports/world_class_submission_review.json b/reports/world_class_submission_review.json new file mode 100644 index 0000000..a4b5ae8 --- /dev/null +++ b/reports/world_class_submission_review.json @@ -0,0 +1,442 @@ +{ + "schema_version": "1.0", + "ok": true, + "generated_at": "2026-07-02", + "skill_dir": ".", + "decision": "awaiting-submissions", + "ready_to_claim_world_class": false, + "review_item_count": 4, + "accepted_count": 0, + "awaiting_submission_count": 4, + "valid_packet_source_incomplete_count": 0, + "ready_for_ledger_review_count": 0, + "fix_submission_count": 0, + "unmatched_submission_count": 0, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "review_counts_submission_as_completion": false, + "summary": { + "review_item_count": 4, + "accepted_count": 0, + "awaiting_submission_count": 4, + "valid_packet_source_incomplete_count": 0, + "ready_for_ledger_review_count": 0, + "fix_submission_count": 0, + "unmatched_submission_count": 0, + "invalid_submission_count": 0, + "source_check_count": 19, + "source_pass_count": 12, + "source_blocked_count": 7, + "ready_to_claim_world_class": false, + "review_counts_submission_as_completion": false, + "decision": "awaiting-submissions" + }, + "report_contract": { + "schema_version": "1.0", + "contract": "world-class-submission-review", + "top_level_mirrors_summary": true, + "summary_fields": [ + "decision", + "ready_to_claim_world_class", + "review_item_count", + "accepted_count", + "awaiting_submission_count", + "valid_packet_source_incomplete_count", + "ready_for_ledger_review_count", + "fix_submission_count", + "unmatched_submission_count", + "invalid_submission_count", + "source_check_count", + "source_pass_count", + "source_blocked_count", + "review_counts_submission_as_completion" + ], + "source_of_truth": "summary" + }, + "submissions": { + "directory": "evidence/world_class/submissions", + "review_counts_submission_as_completion": false + }, + "items": [ + { + "evidence_key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "owner": "operator with provider credentials", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": true, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/provider-holdout.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "model_executed_count": 10, + "timing_observed_count": 10, + "token_observed_count": 10, + "accepted": true + }, + "source_checklist": [ + { + "evidence_key": "provider-holdout", + "label": "Provider model run", + "field": "model_executed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Run provider-backed output-exec with real credentials." + }, + { + "evidence_key": "provider-holdout", + "label": "Timing observed", + "field": "timing_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should record timing metadata." + }, + { + "evidence_key": "provider-holdout", + "label": "Token usage observed", + "field": "token_observed_count", + "expected": ">0", + "actual": 10, + "status": "pass", + "source_accepted": true, + "next_action": "Provider execution should return non-estimated token usage." + } + ], + "source_check_count": 3, + "source_pass_count": 3, + "source_blocked_count": 0, + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass" + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts." + ], + "next_action": "Run provider-backed holdout cases with real credentials and commit only aggregate evidence." + }, + { + "evidence_key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "owner": "human reviewer", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": false, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/human-adjudication.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "pair_count": 5, + "judgment_count": 0, + "pending_count": 5, + "invalid_decision_count": 0, + "answer_revealed_count": 0, + "reviewer_metadata_present": false, + "reason_required": true, + "blind_review_attested": false, + "raw_content_excluded_attested": true, + "reviewer_reason_required_attested": true, + "ready_for_human_evidence": false, + "raw_content_allowed": false, + "raw_content_path_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "human-adjudication", + "label": "Review pairs exist", + "field": "pair_count", + "expected": ">0", + "actual": 5, + "status": "pass", + "source_accepted": false, + "next_action": "Generate the blind A/B review pack." + }, + { + "evidence_key": "human-adjudication", + "label": "No pending decisions", + "field": "pending_count", + "expected": "==0", + "actual": 5, + "status": "blocked", + "source_accepted": false, + "next_action": "Record a reviewer choice and reason for every pair." + }, + { + "evidence_key": "human-adjudication", + "label": "Judgments complete", + "field": "judgment_count", + "expected": "==pair_count", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Every pair needs one valid human judgment." + }, + { + "evidence_key": "human-adjudication", + "label": "No invalid decisions", + "field": "invalid_decision_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Fix malformed winner/confidence entries." + }, + { + "evidence_key": "human-adjudication", + "label": "Reviewer metadata", + "field": "reviewer_metadata_present", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Record reviewer and reviewed_at before adjudication can count." + }, + { + "evidence_key": "human-adjudication", + "label": "Reason required", + "field": "reason_required", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Keep reason mandatory for every imported or direct reviewer decision." + }, + { + "evidence_key": "human-adjudication", + "label": "Blind review attested", + "field": "blind_review_attested", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Set reviewer_attestation only after choices are completed before opening the answer key." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content attested", + "field": "raw_content_excluded_attested", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content." + }, + { + "evidence_key": "human-adjudication", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages." + }, + { + "evidence_key": "human-adjudication", + "label": "Human evidence ready", + "field": "ready_for_human_evidence", + "expected": "true", + "actual": false, + "status": "blocked", + "source_accepted": false, + "next_action": "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints." + } + ], + "source_check_count": 10, + "source_pass_count": 5, + "source_blocked_count": 5, + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass" + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded." + ], + "next_action": "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Native Permission Enforcement", + "category": "external", + "owner": "target client or installer integrator", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": false, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-permission-enforcement.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "native_enforcement_count": 0, + "metadata_fallback_count": 4, + "installer_enforcement_pass_count": 4, + "installer_permission_failure_count": 0, + "installer_enforcement_ready": true, + "residual_risk_count": 4, + "failure_count": 0, + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-permission-enforcement", + "label": "Native enforcement", + "field": "native_enforcement_count", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Collect real target-client or external runtime guard proof." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Probe failures", + "field": "failure_count", + "expected": "==0", + "actual": 0, + "status": "pass", + "source_accepted": false, + "next_action": "Runtime permission probes must stay clean." + }, + { + "evidence_key": "native-permission-enforcement", + "label": "Installer support", + "field": "installer_enforcement_ready", + "expected": "true", + "actual": true, + "status": "pass", + "source_accepted": false, + "next_action": "Installer enforcement is supporting evidence, not native proof." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass" + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement." + ], + "next_action": "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Native Client Telemetry", + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "ledger_status": "pending", + "submission_status": "missing", + "intake_status": "missing", + "source_accepted": false, + "review_state": "awaiting-submission", + "blocking_reason": "No evidence packet has been submitted for review.", + "submission_path": "evidence/world_class/submissions/native-client-telemetry.json", + "submitted_by": "", + "submitted_at": "", + "artifact_ref_count": 0, + "intake_errors": [], + "observed_state": { + "external_source_events": 0, + "adoption_sample_count": 1, + "raw_content_allowed": false, + "risk_band": "low", + "accepted": false + }, + "source_checklist": [ + { + "evidence_key": "native-client-telemetry", + "label": "External events", + "field": "external_source_events", + "expected": ">0", + "actual": 0, + "status": "blocked", + "source_accepted": false, + "next_action": "Import at least one metadata-only event from a real client." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Adoption sample", + "field": "adoption_sample_count", + "expected": ">0", + "actual": 1, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must include adoption outcome evidence." + }, + { + "evidence_key": "native-client-telemetry", + "label": "Raw content blocked", + "field": "raw_content_allowed", + "expected": "false", + "actual": false, + "status": "pass", + "source_accepted": false, + "next_action": "Telemetry must stay metadata-only." + } + ], + "source_check_count": 3, + "source_pass_count": 2, + "source_blocked_count": 1, + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass" + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field." + ], + "next_action": "Install a real client against the native host and import production metadata-only events." + } + ], + "unmatched_submissions": [], + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json" + }, + "artifacts": { + "json": "reports/world_class_submission_review.json", + "markdown": "reports/world_class_submission_review.md" + } +} diff --git a/reports/world_class_submission_review.md b/reports/world_class_submission_review.md new file mode 100644 index 0000000..c1a708f --- /dev/null +++ b/reports/world_class_submission_review.md @@ -0,0 +1,178 @@ +# World-Class Submission Review + +Generated at: `2026-07-02` + +## Summary + +- decision: `awaiting-submissions` +- review items: `4` +- accepted: `0` +- awaiting submission: `4` +- valid packet but source incomplete: `0` +- ready for ledger review: `0` +- fix submission: `0` +- unmatched submissions: `0` +- ready to claim world-class: `false` +- review counts submission as completion: `false` + +This report is a read-only reviewer queue. It does not accept evidence or make world-class completion true. + +## Queue + +| Evidence | Review state | Intake | Source accepted | Submission | Next action | +| --- | --- | --- | --- | --- | --- | +| `provider-holdout` | `awaiting-submission` | `missing` | `true` | `missing` | Run provider-backed holdout cases with real credentials and commit only aggregate evidence. | +| `human-adjudication` | `awaiting-submission` | `missing` | `false` | `missing` | Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication. | +| `native-permission-enforcement` | `awaiting-submission` | `missing` | `false` | `missing` | Integrate a real target-client or external installer runtime guard before claiming native permission enforcement. | +| `native-client-telemetry` | `awaiting-submission` | `missing` | `false` | `missing` | Install a real client against the native host and import production metadata-only events. | + +## Details + +### Provider Holdout + +- review state: `awaiting-submission` +- blocking reason: No evidence packet has been submitted for review. +- ledger status: `pending` +- submission status: `missing` +- intake status: `missing` +- source accepted: `true` +- submission path: `evidence/world_class/submissions/provider-holdout.json` + +#### Source Checks + +- Provider model run: 10 / >0 => pass +- Timing observed: 10 / >0 => pass +- Token usage observed: 10 / >0 => pass + +#### Completion Assertions + +- reports/output_execution_runs.json summary.model_executed_count > 0 +- reports/output_execution_runs.json summary.timing_observed_count > 0 +- reports/output_execution_runs.json summary.token_observed_count > 0 +- reports/skill_os2_audit.json item provider-holdout status becomes pass + +#### Intake Errors + +- No intake errors. + +#### Privacy Contract + +- Do not commit provider credentials or environment dumps. +- The output execution report records output hashes and aggregate run metadata, not raw provider prompts. + +### Human Adjudication + +- review state: `awaiting-submission` +- blocking reason: No evidence packet has been submitted for review. +- ledger status: `pending` +- submission status: `missing` +- intake status: `missing` +- source accepted: `false` +- submission path: `evidence/world_class/submissions/human-adjudication.json` + +#### Source Checks + +- Review pairs exist: 5 / >0 => pass +- No pending decisions: 5 / ==0 => blocked +- Judgments complete: 0 / ==pair_count => blocked +- No invalid decisions: 0 / ==0 => pass +- Reviewer metadata: False / true => blocked +- Reason required: True / true => pass +- Blind review attested: False / true => blocked +- Raw content attested: True / true => pass +- Raw content blocked: False / false => pass +- Human evidence ready: False / true => blocked + +#### Completion Assertions + +- reports/output_review_adjudication.json summary.pending_count == 0 +- reports/output_review_adjudication.json summary.judgment_count == summary.pair_count +- reports/output_review_adjudication.json summary.invalid_decision_count == 0 +- reports/output_review_adjudication.json summary.reviewer_metadata_present is true +- reports/output_review_adjudication.json summary.blind_review_attested is true +- reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json +- reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text +- reports/output_review_adjudication.json summary.ready_for_human_evidence is true +- reports/skill_os2_audit.json item human-adjudication status becomes pass + +#### Intake Errors + +- No intake errors. + +#### Privacy Contract + +- Reviewer decisions should not include raw user data or private customer detail. +- Reviewer reasons must be rubric-based and must not include raw user data or private customer detail. +- The decision importer rejects raw prompt, output, transcript, message, and answer-key fields. +- The adjudication evidence stores prompt_sha256 instead of raw prompt text. +- The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged. +- Keep the answer key separate until after decisions are recorded. + +### Native Permission Enforcement + +- review state: `awaiting-submission` +- blocking reason: No evidence packet has been submitted for review. +- ledger status: `pending` +- submission status: `missing` +- intake status: `missing` +- source accepted: `false` +- submission path: `evidence/world_class/submissions/native-permission-enforcement.json` + +#### Source Checks + +- Native enforcement: 0 / >0 => blocked +- Probe failures: 0 / ==0 => pass +- Installer support: True / true => pass + +#### Completion Assertions + +- reports/runtime_permission_probes.json summary.native_enforcement_count > 0 +- reports/runtime_permission_probes.json summary.failure_count == 0 +- reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence +- reports/skill_os2_audit.json item native-permission-enforcement status becomes pass + +#### Intake Errors + +- No intake errors. + +#### Privacy Contract + +- Do not mark native_enforcement true for metadata-only fallbacks. +- Keep residual risks visible for targets that still rely on operator enforcement. + +### Native Client Telemetry + +- review state: `awaiting-submission` +- blocking reason: No evidence packet has been submitted for review. +- ledger status: `pending` +- submission status: `missing` +- intake status: `missing` +- source accepted: `false` +- submission path: `evidence/world_class/submissions/native-client-telemetry.json` + +#### Source Checks + +- External events: 0 / >0 => blocked +- Adoption sample: 1 / >0 => pass +- Raw content blocked: False / false => pass + +#### Completion Assertions + +- reports/adoption_drift_report.json summary.source_types.external > 0 +- reports/adoption_drift_report.json summary.adoption_sample_count > 0 +- reports/skill_os2_audit.json item native-client-telemetry status becomes pass + +#### Intake Errors + +- No intake errors. + +#### Privacy Contract + +- Telemetry must remain metadata-only and local-first. +- Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field. + +## Boundary + +- A valid submission packet is not accepted evidence by itself. +- Planned work, metadata fallback, pending human review, and local command-runner output still do not count. +- The world-class ledger remains the source of truth for `ready_to_claim_world_class`. diff --git a/requirements-ci.txt b/requirements-ci.txt new file mode 100644 index 0000000..f62ce0c --- /dev/null +++ b/requirements-ci.txt @@ -0,0 +1 @@ +PyYAML==6.0.3 diff --git a/runtime/conformance/README.md b/runtime/conformance/README.md new file mode 100644 index 0000000..0b909c5 --- /dev/null +++ b/runtime/conformance/README.md @@ -0,0 +1,19 @@ +# Runtime Conformance + +Runtime Conformance verifies that a skill package can be consumed by target clients without silently losing its core contract. + +The v0 suite checks: + +- frontmatter `name` and `description` +- description length for Agent Skills / VS Code style clients +- `manifest.json` governance metadata +- `agents/interface.yaml` compatibility metadata +- Skill IR semantic alignment +- target degradation notes +- relative resource paths in IR + +Run: + +```bash +python3 scripts/run_conformance_suite.py . +``` diff --git a/runtime/conformance/schema.json b/runtime/conformance/schema.json new file mode 100644 index 0000000..61f50c6 --- /dev/null +++ b/runtime/conformance/schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.example/runtime-conformance-report.schema.json", + "title": "Yao Runtime Conformance Report", + "description": "Runtime target conformance matrix for Skill OS 2.0.", + "type": "object", + "additionalProperties": false, + "required": ["ok", "skill", "targets", "summary", "artifacts"], + "properties": { + "ok": {"type": "boolean"}, + "skill": {"type": "string"}, + "targets": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["target", "status", "checks", "failures", "warnings"], + "properties": { + "target": {"type": "string"}, + "status": {"enum": ["pass", "fail"]}, + "checks": { + "type": "array", + "items": {"type": "string"} + }, + "failures": { + "type": "array", + "items": {"type": "string"} + }, + "warnings": { + "type": "array", + "items": {"type": "string"} + } + } + } + }, + "summary": { + "type": "object", + "additionalProperties": false, + "required": ["target_count", "pass_count", "fail_count"], + "properties": { + "target_count": {"type": "integer"}, + "pass_count": {"type": "integer"}, + "fail_count": {"type": "integer"} + } + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "required": ["json", "markdown"], + "properties": { + "json": {"type": "string"}, + "markdown": {"type": "string"} + } + } + } +} diff --git a/schemas/adaptation-proposal.schema.json b/schemas/adaptation-proposal.schema.json new file mode 100644 index 0000000..2fbc999 --- /dev/null +++ b/schemas/adaptation-proposal.schema.json @@ -0,0 +1,226 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.local/schemas/adaptation-proposal.schema.json", + "title": "Yao Adaptation Proposal Report", + "type": "object", + "required": [ + "schema_version", + "ok", + "generated_at", + "skill_dir", + "pattern_count", + "proposal_count", + "apply_supported", + "failure_count", + "proposal_only", + "approval_required", + "writes_repository_files", + "approval_draft_supported", + "summary", + "proposal_contract", + "report_contract", + "proposals", + "artifacts" + ], + "properties": { + "schema_version": { + "const": "1.0" + }, + "ok": { + "type": "boolean" + }, + "generated_at": { + "type": "string" + }, + "skill_dir": { + "type": "string" + }, + "pattern_count": { + "type": "integer", + "minimum": 0 + }, + "proposal_count": { + "type": "integer", + "minimum": 0 + }, + "apply_supported": { + "type": "boolean" + }, + "failure_count": { + "type": "integer", + "minimum": 0 + }, + "proposal_only": { + "const": true + }, + "approval_required": { + "const": true + }, + "writes_repository_files": { + "const": false + }, + "approval_draft_supported": { + "const": true + }, + "summary": { + "type": "object", + "required": ["pattern_count", "proposal_count", "apply_supported"], + "properties": { + "pattern_count": { + "type": "integer", + "minimum": 0 + }, + "proposal_count": { + "type": "integer", + "minimum": 0 + }, + "apply_supported": { + "type": "boolean" + } + }, + "additionalProperties": true + }, + "proposal_contract": { + "type": "object", + "required": [ + "proposal_only", + "approval_required", + "writes_repository_files", + "allowlisted_targets_required", + "target_file_sha256_required_for_apply", + "approval_draft_supported", + "rollback_required_for_apply", + "apply_command_available" + ], + "properties": { + "proposal_only": { + "const": true + }, + "approval_required": { + "const": true + }, + "writes_repository_files": { + "const": false + }, + "allowlisted_targets_required": { + "const": true + }, + "target_file_sha256_required_for_apply": { + "const": true + }, + "approval_draft_supported": { + "const": true + }, + "rollback_required_for_apply": { + "const": true + }, + "apply_command_available": { + "type": "boolean" + } + }, + "additionalProperties": true + }, + "report_contract": { + "type": "object", + "required": [ + "schema_version", + "contract", + "top_level_mirrors_summary", + "top_level_mirrors_proposal_contract", + "summary_fields", + "proposal_contract_fields", + "source_of_truth" + ], + "properties": { + "schema_version": { + "const": "1.0" + }, + "contract": { + "const": "adaptation-proposals" + }, + "top_level_mirrors_summary": { + "const": true + }, + "top_level_mirrors_proposal_contract": { + "const": true + }, + "summary_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "proposal_contract_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "source_of_truth": { + "type": "array", + "items": { + "enum": [ + "summary", + "proposal_contract" + ] + } + } + }, + "additionalProperties": true + }, + "proposals": { + "type": "array", + "items": { + "type": "object", + "required": [ + "proposal_id", + "status", + "requires_approval", + "write_allowed_without_approval", + "target_files", + "verification_commands", + "rollback_plan" + ], + "properties": { + "proposal_id": { + "type": "string", + "minLength": 1 + }, + "status": { + "const": "proposal-only" + }, + "requires_approval": { + "const": true + }, + "write_allowed_without_approval": { + "const": false + }, + "risk_level": { + "enum": ["low", "medium", "high"] + }, + "target_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "verification_commands": { + "type": "array", + "items": { + "type": "string" + } + }, + "rollback_plan": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": true + } + }, + "artifacts": { + "type": "object" + } + }, + "additionalProperties": true +} diff --git a/schemas/skill-interpretation.schema.json b/schemas/skill-interpretation.schema.json new file mode 100644 index 0000000..7a29517 --- /dev/null +++ b/schemas/skill-interpretation.schema.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.local/schemas/skill-interpretation.schema.json", + "title": "Yao Skill Interpretation Report", + "type": "object", + "required": [ + "skill_summary", + "scorecard", + "capability_profile", + "principle_model", + "contract_boundary", + "quality_review", + "risk_governance", + "package_assets", + "iteration_roadmap", + "report_contract", + "interpretation_contract" + ], + "properties": { + "report_contract": { + "type": "object", + "required": [ + "schema_version", + "report_kind", + "html_report", + "json_report", + "default_language", + "languages", + "layout", + "nav_labels" + ], + "properties": { + "schema_version": { + "const": "2.0" + }, + "report_kind": { + "const": "skill-interpretation" + }, + "html_report": { + "const": "reports/skill-interpretation.html" + }, + "json_report": { + "const": "reports/skill-interpretation.json" + }, + "default_language": { + "const": "zh-CN" + }, + "languages": { + "type": "array", + "prefixItems": [ + { + "const": "zh-CN" + }, + { + "const": "en" + } + ], + "minItems": 2 + }, + "layout": { + "const": "kami-white-audit-v2" + }, + "nav_labels": { + "type": "array", + "minItems": 9, + "items": { + "type": "string" + } + } + }, + "additionalProperties": true + }, + "interpretation_contract": { + "type": "object", + "required": [ + "schema_version", + "source_model", + "source_model_reused", + "overview_report", + "html_report", + "json_report", + "includes" + ], + "properties": { + "schema_version": { + "const": "2.0" + }, + "source_model": { + "const": "skill-overview-v2" + }, + "source_model_reused": { + "const": true + }, + "overview_report": { + "const": "reports/skill-overview.html" + }, + "html_report": { + "const": "reports/skill-interpretation.html" + }, + "json_report": { + "const": "reports/skill-interpretation.json" + }, + "includes": { + "type": "array", + "minItems": 10, + "items": { + "type": "string" + } + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true +} diff --git a/schemas/skillops-opportunity.schema.json b/schemas/skillops-opportunity.schema.json new file mode 100644 index 0000000..ed82453 --- /dev/null +++ b/schemas/skillops-opportunity.schema.json @@ -0,0 +1,112 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.local/schemas/skillops-opportunity.schema.json", + "title": "SkillOps Opportunity", + "type": "object", + "required": [ + "opportunity_id", + "pattern_id", + "title", + "opportunity_type", + "action_type", + "decision", + "priority", + "score", + "risk_level", + "requires_approval", + "write_allowed_without_approval", + "evidence_count", + "target_files", + "verification_commands" + ], + "properties": { + "opportunity_id": { + "type": "string", + "pattern": "^skillops-[a-f0-9]{10}$" + }, + "proposal_id": { + "type": "string" + }, + "pattern_id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "opportunity_type": { + "type": "string" + }, + "action_type": { + "type": "string", + "enum": [ + "add_eval", + "agents_update", + "patch_existing_skill", + "report_only" + ] + }, + "decision": { + "type": "string", + "enum": [ + "ready_for_approval_review", + "proposal_review", + "observe_more_evidence", + "report_only", + "no_action" + ] + }, + "priority": { + "type": "string", + "enum": [ + "high", + "medium", + "low" + ] + }, + "score": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "score_reasons": { + "type": "array", + "items": { + "type": "string" + } + }, + "policy_reason": { + "type": "string" + }, + "risk_level": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "requires_approval": { + "type": "boolean" + }, + "write_allowed_without_approval": { + "const": false + }, + "evidence_count": { + "type": "integer", + "minimum": 0 + }, + "target_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "verification_commands": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": true +} diff --git a/scripts/adaptation_patch_safety.py b/scripts/adaptation_patch_safety.py new file mode 100644 index 0000000..83a8ecd --- /dev/null +++ b/scripts/adaptation_patch_safety.py @@ -0,0 +1,76 @@ +"""Patch target and baseline hash checks for approval-gated adaptation.""" + +import hashlib +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Shared adaptation patch target parsing and target-file baseline verification helpers." + +BLOCKED_PATH_PARTS = {".git", "__pycache__", ".pytest_cache", "dist"} +ABSENT_FILE_SHA256 = "__absent__" + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def target_file_sha256(skill_dir: Path, target_files: list[str]) -> dict[str, str]: + observed: dict[str, str] = {} + for target in target_files: + path = skill_dir / target + observed[target] = sha256_file(path) if path.exists() else ABSENT_FILE_SHA256 + return observed + + +def normalize_patch_path(raw: str) -> str | None: + token = raw.strip().split("\t", 1)[0].split(" ", 1)[0] + if token == "/dev/null": + return None + if token.startswith("a/") or token.startswith("b/"): + token = token[2:] + path = Path(token) + if path.is_absolute() or ".." in path.parts or any(part in BLOCKED_PATH_PARTS for part in path.parts): + raise ValueError(f"Unsafe patch path: {raw}") + if not token or token == ".": + raise ValueError(f"Empty patch path: {raw}") + return token + + +def patch_target_files(patch_text: str) -> list[str]: + targets: set[str] = set() + for line in patch_text.splitlines(): + if line.startswith("--- ") or line.startswith("+++ "): + raw = line[4:].strip() + path = normalize_patch_path(raw) + if path: + targets.add(path) + return sorted(targets) + + +def validate_target_file_sha256( + skill_dir: Path, + target_files: list[str], + expected_sha256: dict[str, Any], +) -> tuple[list[str], dict[str, str]]: + failures: list[str] = [] + observed: dict[str, str] = {} + for target in target_files: + path = skill_dir / target + expected = str(expected_sha256.get(target, "")) + if not expected: + failures.append(f"Approval target_file_sha256 missing target: {target}") + continue + if path.exists() and not path.is_file(): + failures.append(f"Patch target is not a file: {target}") + continue + current = sha256_file(path) if path.exists() else ABSENT_FILE_SHA256 + observed[target] = current + if current != expected: + failures.append(f"Target file baseline sha256 does not match approval ledger: {target}") + return failures, observed diff --git a/scripts/adaptation_report_contracts.py b/scripts/adaptation_report_contracts.py new file mode 100644 index 0000000..9114bd8 --- /dev/null +++ b/scripts/adaptation_report_contracts.py @@ -0,0 +1,111 @@ +"""Shared report contracts for approval-gated skill adaptation.""" + +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Shared adaptation approval and regression report contract decoration helpers." + +APPROVAL_SUMMARY_FIELDS = [ + "approval_count", + "active_approval_count", + "pending_review_count", + "applied_count", + "rollback_count", +] +APPROVAL_CONTRACT_FIELDS = [ + "approval_required", + "patch_sha256_required", + "allowlisted_targets_required", + "target_file_sha256_required", + "approval_draft_supported", + "dry_run_default", + "writes_repository_files_only_with_apply", + "rollback_required", +] +REGRESSION_SUMMARY_FIELDS = [ + "apply_supported", + "attempt_count", + "approval_draft_count", + "applied_count", + "dry_run_count", + "rollback_count", + "regression_run_count", + "regression_pass_count", + "failure_count", +] +APPLY_CONTRACT_FIELDS = [ + *APPROVAL_CONTRACT_FIELDS, + "safe_regression_commands_only", + "rollback_on_failure_default", +] +APPROVAL_CONTRACT = { + "approval_required": True, + "patch_sha256_required": True, + "allowlisted_targets_required": True, + "target_file_sha256_required": True, + "approval_draft_supported": True, + "dry_run_default": True, + "writes_repository_files_only_with_apply": True, + "rollback_required": True, +} +APPLY_CONTRACT = { + **APPROVAL_CONTRACT, + "safe_regression_commands_only": True, + "rollback_on_failure_default": True, +} + + +def top_level_mirrors( + summary: dict[str, Any], + contract: dict[str, Any], + summary_fields: list[str], + contract_fields: list[str], +) -> dict[str, Any]: + mirrored = {key: summary[key] for key in summary_fields if key in summary} + mirrored.update({key: contract[key] for key in contract_fields if key in contract}) + return mirrored + + +def report_contract(name: str, contract_key: str, summary_fields: list[str], contract_fields: list[str]) -> dict[str, Any]: + return { + "schema_version": "1.0", + "contract": name, + "top_level_mirrors_summary": True, + f"top_level_mirrors_{contract_key}": True, + "summary_fields": summary_fields, + f"{contract_key}_fields": contract_fields, + "source_of_truth": ["summary", contract_key], + } + + +def decorate_approval_ledger(ledger: dict[str, Any]) -> dict[str, Any]: + summary = ledger.get("summary", {}) if isinstance(ledger.get("summary"), dict) else {} + approval_contract = ( + ledger.get("approval_contract", {}) if isinstance(ledger.get("approval_contract"), dict) else {} + ) or dict(APPROVAL_CONTRACT) + ledger.update(top_level_mirrors(summary, approval_contract, APPROVAL_SUMMARY_FIELDS, APPROVAL_CONTRACT_FIELDS)) + ledger["approval_contract"] = approval_contract + ledger["report_contract"] = report_contract( + "adaptation-approval-ledger", + "approval_contract", + APPROVAL_SUMMARY_FIELDS, + APPROVAL_CONTRACT_FIELDS, + ) + return ledger + + +def decorate_regression_report(report: dict[str, Any]) -> dict[str, Any]: + summary = report.get("summary", {}) if isinstance(report.get("summary"), dict) else {} + apply_contract = ( + report.get("apply_contract", {}) if isinstance(report.get("apply_contract"), dict) else {} + ) or dict(APPLY_CONTRACT) + report.update(top_level_mirrors(summary, apply_contract, REGRESSION_SUMMARY_FIELDS, APPLY_CONTRACT_FIELDS)) + report["apply_contract"] = apply_contract + report["report_contract"] = report_contract( + "adaptation-regression-report", + "apply_contract", + REGRESSION_SUMMARY_FIELDS, + APPLY_CONTRACT_FIELDS, + ) + return report diff --git a/scripts/adjudicate_output_review.py b/scripts/adjudicate_output_review.py new file mode 100644 index 0000000..69ac465 --- /dev/null +++ b/scripts/adjudicate_output_review.py @@ -0,0 +1,566 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_BLIND_PACK = ROOT / "reports" / "output_blind_review_pack.json" +DEFAULT_ANSWER_KEY = ROOT / "reports" / "output_blind_answer_key.json" +DEFAULT_DECISIONS = ROOT / "reports" / "output_review_decisions.json" +DEFAULT_OUTPUT_JSON = ROOT / "reports" / "output_review_adjudication.json" +DEFAULT_OUTPUT_MD = ROOT / "reports" / "output_review_adjudication.md" + + +def display_path(path: Path) -> str: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +def load_json(path: Path) -> tuple[dict[str, Any], list[str]]: + if not path.exists(): + return {}, [f"Missing JSON file: {display_path(path)}"] + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + return {}, [f"Invalid JSON file {display_path(path)}: {exc}"] + if not isinstance(payload, dict): + return {}, [f"JSON root must be an object: {display_path(path)}"] + return payload, [] + + +def load_optional_decisions(path: Path) -> tuple[dict[str, Any], list[str]]: + if not path.exists(): + return {"schema_version": "1.0", "decisions": []}, [] + return load_json(path) + + +def normalize_variant(value: Any) -> str: + normalized = str(value or "").strip().upper() + if normalized in {"VARIANT A", "A"}: + return "A" + if normalized in {"VARIANT B", "B"}: + return "B" + return normalized + + +def confidence_value(value: Any) -> tuple[float | None, str | None]: + if value is None or value == "": + return None, None + try: + parsed = float(value) + except (TypeError, ValueError): + return None, f"confidence must be numeric, got {value!r}" + if parsed < 0 or parsed > 1: + return None, f"confidence must be between 0 and 1, got {parsed}" + return round(parsed, 3), None + + +def prompt_sha256(pair: dict[str, Any]) -> str: + return hashlib.sha256(str(pair.get("prompt", "")).encode("utf-8")).hexdigest() + + +def canonical_sha256(value: Any) -> str: + encoded = json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8") + return hashlib.sha256(encoded).hexdigest() + + +def review_integrity(blind_pack: dict[str, Any]) -> dict[str, Any]: + pairs = blind_pack.get("pairs", []) + case_ids: list[str] = [] + prompt_hashes: dict[str, str] = {} + if isinstance(pairs, list): + for pair in pairs: + if not isinstance(pair, dict): + continue + case_id = str(pair.get("case_id", "")).strip() + if not case_id: + continue + case_ids.append(case_id) + prompt_hashes[case_id] = prompt_sha256(pair) + return { + "blind_pack_sha256": canonical_sha256(blind_pack), + "case_count": len(case_ids), + "case_ids": case_ids, + "prompt_sha256_by_case": prompt_hashes, + } + + +def default_reviewer_attestation() -> dict[str, Any]: + return { + "blind_review_completed_before_answer_key": False, + "answer_key_not_opened_before_decisions": False, + "raw_content_excluded": True, + "reviewer_reason_required": True, + } + + +def answer_index(answer_key: dict[str, Any]) -> dict[str, dict[str, Any]]: + answers = answer_key.get("answers", []) + if not isinstance(answers, list): + return {} + return {str(item.get("case_id", "")): item for item in answers if isinstance(item, dict) and item.get("case_id")} + + +def pair_index(blind_pack: dict[str, Any]) -> dict[str, dict[str, Any]]: + pairs = blind_pack.get("pairs", []) + if not isinstance(pairs, list): + return {} + return {str(item.get("case_id", "")): item for item in pairs if isinstance(item, dict) and item.get("case_id")} + + +def decision_index(decisions_payload: dict[str, Any]) -> tuple[dict[str, dict[str, Any]], list[str]]: + decisions = decisions_payload.get("decisions", []) + failures: list[str] = [] + indexed: dict[str, dict[str, Any]] = {} + if not isinstance(decisions, list): + return {}, ["decisions must be a list"] + for index, item in enumerate(decisions, start=1): + if not isinstance(item, dict): + failures.append(f"decision #{index} must be an object") + continue + case_id = str(item.get("case_id", "")).strip() + if not case_id: + failures.append(f"decision #{index} is missing case_id") + continue + if case_id in indexed: + failures.append(f"duplicate decision for case_id: {case_id}") + continue + indexed[case_id] = item + return indexed, failures + + +def build_decision_template(blind_pack: dict[str, Any]) -> dict[str, Any]: + pairs = blind_pack.get("pairs", []) + template_decisions = [] + if isinstance(pairs, list): + for pair in pairs: + if not isinstance(pair, dict): + continue + template_decisions.append( + { + "case_id": str(pair.get("case_id", "")), + "winner_variant": "", + "confidence": None, + "reason": "", + } + ) + return { + "schema_version": "1.0", + "reviewer": "", + "reviewed_at": "", + "review_integrity": review_integrity(blind_pack), + "reviewer_attestation": default_reviewer_attestation(), + "decision_contract": { + "winner_variant": "Use A or B after reading the blind review pack. Leave blank when pending.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required reviewer rationale. Do not reveal baseline or with-skill labels before adjudication.", + "reviewer_attestation": "Set blind_review_completed_before_answer_key and answer_key_not_opened_before_decisions to true only after a real blind review.", + }, + "decisions": template_decisions, + } + + +def adjudicate_pair( + case_id: str, + pair: dict[str, Any], + answer: dict[str, Any] | None, + decision: dict[str, Any] | None, + reviewer_metadata_present: bool = True, +) -> tuple[dict[str, Any], list[str]]: + failures: list[str] = [] + expected = normalize_variant(answer.get("expected_winner_variant", "") if answer else "") + if expected not in {"A", "B"}: + failures.append(f"{case_id}: answer key is missing expected_winner_variant") + if decision is None: + return ( + { + "case_id": case_id, + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": False, + "reviewer_winner_variant": "", + "confidence": None, + "reason": "", + "prompt_sha256": prompt_sha256(pair), + }, + failures, + ) + + reviewer = normalize_variant( + decision.get("winner_variant", decision.get("reviewer_winner_variant", decision.get("winner", ""))) + ) + confidence, confidence_failure = confidence_value(decision.get("confidence")) + reason = str(decision.get("reason", "")).strip() + if not reviewer: + return ( + { + "case_id": case_id, + "status": "pending", + "expected_winner_variant": "", + "expected_revealed": False, + "reviewer_winner_variant": "", + "confidence": confidence, + "reason": reason, + "prompt_sha256": prompt_sha256(pair), + }, + failures, + ) + if expected not in {"A", "B"}: + status = "invalid" + elif reviewer not in {"A", "B"}: + failures.append(f"{case_id}: winner_variant must be A or B") + status = "invalid" + elif confidence_failure: + failures.append(f"{case_id}: {confidence_failure}") + status = "invalid" + elif not reason: + failures.append(f"{case_id}: reason is required before answer key can be revealed") + status = "invalid" + elif not reviewer_metadata_present: + failures.append(f"{case_id}: reviewer and reviewed_at are required before answer key can be revealed") + status = "invalid" + else: + status = "match" if reviewer == expected else "disagree" + expected_revealed = status in {"match", "disagree"} + return ( + { + "case_id": case_id, + "status": status, + "expected_winner_variant": expected if expected_revealed else "", + "expected_revealed": expected_revealed, + "reviewer_winner_variant": reviewer, + "confidence": confidence, + "reason": reason, + "prompt_sha256": prompt_sha256(pair), + }, + failures, + ) + + +def reviewer_attestation_state(decisions_payload: dict[str, Any]) -> dict[str, bool]: + attestation = decisions_payload.get("reviewer_attestation", {}) + if not isinstance(attestation, dict): + attestation = {} + blind_review_completed = attestation.get("blind_review_completed_before_answer_key") is True + answer_key_not_opened = attestation.get("answer_key_not_opened_before_decisions") is True + raw_content_excluded = attestation.get("raw_content_excluded") is True + reviewer_reason_required = attestation.get("reviewer_reason_required") is True + return { + "blind_review_attested": blind_review_completed and answer_key_not_opened, + "blind_review_completed_before_answer_key": blind_review_completed, + "answer_key_not_opened_before_decisions": answer_key_not_opened, + "raw_content_excluded_attested": raw_content_excluded, + "reviewer_reason_required_attested": reviewer_reason_required, + } + + +def build_summary( + pairs: list[dict[str, Any]], + failures: list[str], + reviewer_metadata_present: bool, + attestation_state: dict[str, bool], +) -> dict[str, Any]: + pair_count = len(pairs) + judgment_count = sum(1 for item in pairs if item["status"] in {"match", "disagree"}) + pending_count = sum(1 for item in pairs if item["status"] == "pending") + agreement_count = sum(1 for item in pairs if item["status"] == "match") + disagreement_count = sum(1 for item in pairs if item["status"] == "disagree") + invalid_decision_count = sum(1 for item in pairs if item["status"] == "invalid") + answer_revealed_count = sum(1 for item in pairs if item.get("expected_revealed")) + agreement_rate = round(agreement_count / judgment_count * 100, 2) if judgment_count else None + return { + "pair_count": pair_count, + "judgment_count": judgment_count, + "pending_count": pending_count, + "agreement_count": agreement_count, + "disagreement_count": disagreement_count, + "invalid_decision_count": invalid_decision_count, + "answer_revealed_count": answer_revealed_count, + "pending_answer_hidden_count": sum(1 for item in pairs if item["status"] in {"pending", "invalid"} and not item.get("expected_revealed")), + "agreement_rate": agreement_rate, + "needs_review": pending_count > 0, + "reviewer_metadata_present": reviewer_metadata_present, + "reason_required": True, + **attestation_state, + "ready_for_human_evidence": bool(pair_count) + and judgment_count == pair_count + and pending_count == 0 + and invalid_decision_count == 0 + and reviewer_metadata_present + and attestation_state["blind_review_attested"] + and attestation_state["raw_content_excluded_attested"] + and attestation_state["reviewer_reason_required_attested"] + and len(failures) == 0, + "failure_count": len(failures), + } + + +def checklist_readiness(pair: dict[str, Any]) -> tuple[str, str]: + status = pair.get("status") + if status in {"match", "disagree"}: + return "adjudicated", "Reviewer decision is valid; answer key is revealed for this case." + if status == "invalid": + return "fix-decision", "Reviewer decision exists but failed validation; answer key remains hidden." + return "awaiting-decision", "Reviewer has not selected A or B yet; answer key remains hidden." + + +def build_reviewer_checklist( + pairs: list[dict[str, Any]], + blind_pack_path: Path, + decisions_path: Path, +) -> list[dict[str, Any]]: + checklist = [] + for pair in pairs: + readiness, blocking_reason = checklist_readiness(pair) + checklist.append( + { + "case_id": pair.get("case_id", ""), + "readiness": readiness, + "blocking_reason": blocking_reason, + "status": pair.get("status", "pending"), + "reviewer_winner_variant": pair.get("reviewer_winner_variant", ""), + "answer_key_visible": bool(pair.get("expected_revealed")), + "prompt_sha256": pair.get("prompt_sha256", ""), + "blind_pack_path": display_path(blind_pack_path), + "decisions_path": display_path(decisions_path), + "commands": { + "prepare_review_kit": "python3 scripts/yao.py output-review-kit", + "write_template": "python3 scripts/adjudicate_output_review.py --write-template", + "import_decisions": "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "adjudicate": "python3 scripts/yao.py output-review", + "refresh_review_studio": "python3 scripts/yao.py review-studio .", + }, + "required_fields": { + "winner_variant": "A or B after reading only the blind review pack.", + "confidence": "Optional number from 0 to 1.", + "reason": "Required rationale; do not reveal baseline or with-skill labels before adjudication.", + "reviewer": "Human reviewer name or review group at the decision-file top level.", + "reviewed_at": "Review date or timestamp at the decision-file top level.", + "reviewer_attestation.blind_review_completed_before_answer_key": "True only after the reviewer has completed choices before opening the answer key.", + "reviewer_attestation.answer_key_not_opened_before_decisions": "True only when the answer key was not opened before decisions were recorded.", + }, + "privacy_contract": [ + "Do not paste raw private user data into the decision reason.", + "Do not open the answer key before reviewer choices are recorded.", + "Leave winner_variant blank when the reviewer is not ready to decide.", + ], + } + ) + return checklist + + +def add_checklist_summary(summary: dict[str, Any], checklist: list[dict[str, Any]]) -> dict[str, Any]: + enriched = dict(summary) + enriched["reviewer_checklist_count"] = len(checklist) + enriched["reviewer_checklist_pending_count"] = sum(1 for item in checklist if item["readiness"] == "awaiting-decision") + enriched["reviewer_checklist_invalid_count"] = sum(1 for item in checklist if item["readiness"] == "fix-decision") + enriched["reviewer_checklist_ready_count"] = sum(1 for item in checklist if item["readiness"] == "adjudicated") + return enriched + + +def render_reviewer_checklist(checklist: list[dict[str, Any]]) -> list[str]: + lines = [ + "## Reviewer Checklist", + "", + "| Case | Readiness | Answer key | Decision file |", + "| --- | --- | --- | --- |", + ] + if not checklist: + lines.append("| `none` | `adjudicated` | n/a | none |") + return lines + for item in checklist: + answer_key = "visible" if item.get("answer_key_visible") else "hidden" + lines.append( + f"| `{item['case_id']}` | `{item['readiness']}` | `{answer_key}` | `{item['decisions_path']}` |" + ) + for item in checklist: + lines.extend(["", f"### {item['case_id']}", ""]) + lines.append(f"- readiness: `{item['readiness']}`") + lines.append(f"- blocking reason: {item['blocking_reason']}") + lines.append(f"- answer key visible: `{str(item['answer_key_visible']).lower()}`") + lines.append(f"- blind pack: `{item['blind_pack_path']}`") + lines.append(f"- decisions: `{item['decisions_path']}`") + lines.extend(["", "#### Commands", ""]) + for label, command in item.get("commands", {}).items(): + lines.append(f"- {label}: `{command}`") + lines.extend(["", "#### Required Fields", ""]) + for label, description in item.get("required_fields", {}).items(): + lines.append(f"- {label}: {description}") + lines.extend(["", "#### Privacy Contract", ""]) + lines.extend(f"- {contract}" for contract in item.get("privacy_contract", [])) + return lines + + +def render_markdown(payload: dict[str, Any]) -> str: + summary = payload["summary"] + lines = [ + "# Output Review Adjudication", + "", + "This report adjudicates reviewer choices from the blind A/B output review pack against the separate answer key.", + "", + f"- Pairs: `{summary['pair_count']}`", + f"- Judgments: `{summary['judgment_count']}`", + f"- Pending: `{summary['pending_count']}`", + f"- Agreement rate: `{summary['agreement_rate'] if summary['agreement_rate'] is not None else 'n/a'}`", + f"- Invalid decisions: `{summary['invalid_decision_count']}`", + f"- Answer keys revealed: `{summary['answer_revealed_count']}`", + f"- Pending/invalid answers hidden: `{summary['pending_answer_hidden_count']}`", + f"- Reviewer checklist: `{summary['reviewer_checklist_ready_count']}` ready / `{summary['reviewer_checklist_count']}` total", + f"- Reviewer metadata present: `{str(summary['reviewer_metadata_present']).lower()}`", + f"- Blind review attested: `{str(summary['blind_review_attested']).lower()}`", + f"- Raw content excluded: `{str(summary['raw_content_excluded_attested']).lower()}`", + f"- Ready for human evidence: `{str(summary['ready_for_human_evidence']).lower()}`", + "", + ] + if summary["judgment_count"] == 0: + lines.extend( + [ + "No reviewer decisions recorded yet.", + "", + "Generate a template with `--write-template`, fill `winner_variant` with `A` or `B`, then rerun adjudication.", + "Expected winners stay hidden until a valid reviewer decision is recorded.", + "", + ] + ) + lines.extend( + [ + "## Case Adjudication", + "", + "| Case | Reviewer | Expected | Status | Confidence | Reason |", + "| --- | --- | --- | --- | ---: | --- |", + ] + ) + for item in payload["pairs"]: + confidence = "" if item.get("confidence") is None else str(item["confidence"]) + reason = str(item.get("reason", "")).replace("|", "\\|") or "" + expected = item.get("expected_winner_variant", "") if item.get("expected_revealed") else "hidden" + lines.append( + f"| {item['case_id']} | {item.get('reviewer_winner_variant', '') or 'pending'} | " + f"{expected} | {item['status']} | {confidence} | {reason} |" + ) + if payload.get("failures"): + lines.extend(["", "## Failures", ""]) + for failure in payload["failures"]: + lines.append(f"- {failure}") + lines.extend(["", *render_reviewer_checklist(payload.get("reviewer_checklist", []))]) + lines.extend( + [ + "", + "## Next Fixes", + "", + "- Keep the blind review pack separate from the answer key until decisions are recorded.", + "- Treat disagreement cases as prompts for rubric tuning or output improvement.", + "- Add model-executed holdout runs after this human adjudication harness is stable.", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def adjudicate_output_review( + blind_pack_path: Path, + answer_key_path: Path, + decisions_path: Path, + output_json: Path, + output_md: Path, + write_template: bool = False, +) -> dict[str, Any]: + blind_pack, failures = load_json(blind_pack_path) + answer_key, answer_failures = load_json(answer_key_path) + failures.extend(answer_failures) + decisions_payload, decision_failures = load_optional_decisions(decisions_path) + failures.extend(decision_failures) + + template_written = False + if write_template and blind_pack and not decisions_path.exists(): + decisions_path.parent.mkdir(parents=True, exist_ok=True) + decisions_path.write_text( + json.dumps(build_decision_template(blind_pack), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + template_written = True + + pairs_by_id = pair_index(blind_pack) + answers_by_id = answer_index(answer_key) + decisions_by_id, index_failures = decision_index(decisions_payload) + failures.extend(index_failures) + reviewer_metadata_present = bool( + str(decisions_payload.get("reviewer", "")).strip() + and str(decisions_payload.get("reviewed_at", "")).strip() + ) + attestation_state = reviewer_attestation_state(decisions_payload) + + for case_id in decisions_by_id: + if case_id not in pairs_by_id: + failures.append(f"decision references unknown case_id: {case_id}") + + adjudicated_pairs: list[dict[str, Any]] = [] + for case_id, pair in pairs_by_id.items(): + adjudicated, pair_failures = adjudicate_pair( + case_id, + pair, + answers_by_id.get(case_id), + decisions_by_id.get(case_id), + reviewer_metadata_present=reviewer_metadata_present, + ) + adjudicated_pairs.append(adjudicated) + failures.extend(pair_failures) + + summary = build_summary(adjudicated_pairs, failures, reviewer_metadata_present, attestation_state) + reviewer_checklist = build_reviewer_checklist(adjudicated_pairs, blind_pack_path, decisions_path) + summary = add_checklist_summary(summary, reviewer_checklist) + payload = { + "schema_version": "1.0", + "ok": not failures, + "summary": summary, + "reviewer": decisions_payload.get("reviewer", ""), + "reviewed_at": decisions_payload.get("reviewed_at", ""), + "review_integrity": review_integrity(blind_pack), + "reviewer_attestation": decisions_payload.get("reviewer_attestation", {}), + "artifacts": { + "blind_pack": display_path(blind_pack_path), + "answer_key": display_path(answer_key_path), + "decisions": display_path(decisions_path), + "json": display_path(output_json), + "markdown": display_path(output_md), + }, + "template_written": template_written, + "pairs": adjudicated_pairs, + "reviewer_checklist": reviewer_checklist, + "failures": failures, + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Adjudicate blind A/B output review decisions against the answer key.") + parser.add_argument("--blind-pack", default=str(DEFAULT_BLIND_PACK)) + parser.add_argument("--answer-key", default=str(DEFAULT_ANSWER_KEY)) + parser.add_argument("--decisions", default=str(DEFAULT_DECISIONS)) + parser.add_argument("--output-json", default=str(DEFAULT_OUTPUT_JSON)) + parser.add_argument("--output-md", default=str(DEFAULT_OUTPUT_MD)) + parser.add_argument("--write-template", action="store_true") + args = parser.parse_args() + + payload = adjudicate_output_review( + Path(args.blind_pack).resolve(), + Path(args.answer_key).resolve(), + Path(args.decisions).resolve(), + Path(args.output_json).resolve(), + Path(args.output_md).resolve(), + write_template=args.write_template, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/apply_adaptation.py b/scripts/apply_adaptation.py new file mode 100644 index 0000000..52ecdc1 --- /dev/null +++ b/scripts/apply_adaptation.py @@ -0,0 +1,543 @@ +#!/usr/bin/env python3 +"""Apply an approved adaptation patch with allowlisted targets and regression evidence.""" + +import argparse +import json +import os +import shlex +import subprocess +import sys +from datetime import date, datetime, timezone +from pathlib import Path +from typing import Any + +from adaptation_patch_safety import patch_target_files, sha256_file, target_file_sha256, validate_target_file_sha256 +from adaptation_report_contracts import APPLY_CONTRACT, APPROVAL_CONTRACT, decorate_approval_ledger, decorate_regression_report + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Approval-gated adaptive patch application with dry-run, allowlist, regression, and rollback evidence." + + +def utc_now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def display_path(path: Path, skill_dir: Path) -> str: + try: + return str(path.resolve().relative_to(skill_dir.resolve())) + except ValueError: + return str(path) + + +def resolve_path(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 approved_entries(ledger: dict[str, Any]) -> list[dict[str, Any]]: + entries = ledger.get("entries") + return [item for item in entries if isinstance(item, dict)] if isinstance(entries, list) else [] + + +def find_proposal(proposals: dict[str, Any], proposal_id: str) -> dict[str, Any]: + for item in proposals.get("proposals", []): + if isinstance(item, dict) and item.get("proposal_id") == proposal_id: + return item + return {} + + +def find_approval(ledger: dict[str, Any], proposal_id: str) -> dict[str, Any]: + for item in approved_entries(ledger): + if item.get("proposal_id") == proposal_id and item.get("decision") == "approved": + return item + return {} + + +def refresh_ledger_summary(ledger: dict[str, Any]) -> None: + entries = approved_entries(ledger) + approved = [item for item in entries if item.get("decision") == "approved"] + pending = [item for item in entries if item.get("decision") == "pending-review"] + ledger["summary"] = { + "approval_count": len(approved), + "active_approval_count": len(approved), + "pending_review_count": len(pending), + "applied_count": 0, + "rollback_count": 0, + } + decorate_approval_ledger(ledger) + + +def upsert_ledger_entry(ledger: dict[str, Any], entry: dict[str, Any]) -> None: + entries = approved_entries(ledger) + proposal_id = entry.get("proposal_id") + kept = [item for item in entries if item.get("proposal_id") != proposal_id] + ledger["entries"] = [*kept, entry] + refresh_ledger_summary(ledger) + + +def parse_date(value: str) -> date | None: + if not value: + return None + try: + return date.fromisoformat(value[:10]) + except ValueError: + return None + + +def validate_approval(approval: dict[str, Any], today: date) -> list[str]: + failures = [] + required = [ + "reviewer", + "reason", + "approved_at", + "patch_sha256", + "target_files", + "target_file_sha256", + "verification_commands", + "rollback_plan", + ] + for key in required: + if not approval.get(key): + failures.append(f"Approval entry missing required field: {key}") + expires_at = parse_date(str(approval.get("expires_at", ""))) + if expires_at and expires_at < today: + failures.append(f"Approval entry is expired: {approval.get('expires_at')}") + if not isinstance(approval.get("target_files"), list): + failures.append("Approval target_files must be a list.") + if not isinstance(approval.get("target_file_sha256"), dict): + failures.append("Approval target_file_sha256 must be an object.") + if not isinstance(approval.get("verification_commands"), list): + failures.append("Approval verification_commands must be a list.") + return failures + + +def safe_command(command: str) -> tuple[bool, list[str], str]: + try: + parts = shlex.split(command) + except ValueError as exc: + return False, [], f"cannot parse command: {exc}" + if not parts: + return False, [], "empty command" + if parts[0] == "make" and len(parts) == 2 and all(ch.isalnum() or ch in {"-", "_"} for ch in parts[1]): + return True, parts, "make target" + if parts[0] in {"python3", sys.executable} and len(parts) >= 2: + script = Path(parts[1]) + if not script.is_absolute() and script.parts and script.parts[0] in {"tests", "scripts"} and script.suffix == ".py": + return True, parts, "local python verifier" + return False, parts, "command is not in the safe regression allowlist" + + +def run_command(command: str, skill_dir: Path) -> dict[str, Any]: + allowed, parts, reason = safe_command(command) + if not allowed: + return {"command": command, "ok": False, "returncode": None, "stdout": "", "stderr": reason} + proc = subprocess.run(parts, cwd=skill_dir, capture_output=True, text=True) + return { + "command": command, + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "stdout": proc.stdout[-4000:], + "stderr": proc.stderr[-4000:], + } + + +def git_apply_check(skill_dir: Path, patch_file: Path) -> dict[str, Any]: + env = dict(os.environ) + env["GIT_CEILING_DIRECTORIES"] = str(skill_dir.parent) + proc = subprocess.run(["git", "apply", "--check", str(patch_file)], cwd=skill_dir, capture_output=True, text=True, env=env) + return {"ok": proc.returncode == 0, "returncode": proc.returncode, "stdout": proc.stdout, "stderr": proc.stderr} + + +def git_apply(skill_dir: Path, patch_file: Path) -> dict[str, Any]: + env = dict(os.environ) + env["GIT_CEILING_DIRECTORIES"] = str(skill_dir.parent) + proc = subprocess.run(["git", "apply", str(patch_file)], cwd=skill_dir, capture_output=True, text=True, env=env) + return {"ok": proc.returncode == 0, "returncode": proc.returncode, "stdout": proc.stdout, "stderr": proc.stderr} + + +def git_apply_reverse(skill_dir: Path, patch_file: Path) -> dict[str, Any]: + env = dict(os.environ) + env["GIT_CEILING_DIRECTORIES"] = str(skill_dir.parent) + proc = subprocess.run(["git", "apply", "-R", str(patch_file)], cwd=skill_dir, capture_output=True, text=True, env=env) + return {"ok": proc.returncode == 0, "returncode": proc.returncode, "stdout": proc.stdout, "stderr": proc.stderr} + + +def empty_approval_ledger(generated_at: str) -> dict[str, Any]: + return decorate_approval_ledger({ + "schema_version": "1.0", + "ok": True, + "generated_at": generated_at, + "summary": { + "approval_count": 0, + "active_approval_count": 0, + "pending_review_count": 0, + "applied_count": 0, + "rollback_count": 0, + }, + "approval_contract": dict(APPROVAL_CONTRACT), + "entries": [], + }) + + +def empty_regression_report(skill_dir: Path, generated_at: str) -> dict[str, Any]: + return decorate_regression_report({ + "schema_version": "1.0", + "ok": True, + "generated_at": generated_at, + "skill_dir": display_path(skill_dir, skill_dir), + "summary": { + "apply_supported": True, + "attempt_count": 0, + "approval_draft_count": 0, + "applied_count": 0, + "dry_run_count": 0, + "rollback_count": 0, + "regression_run_count": 0, + "regression_pass_count": 0, + "failure_count": 0, + }, + "apply_contract": dict(APPLY_CONTRACT), + "attempts": [], + "failures": [], + "artifacts": { + "json": "reports/adaptation_regression_report.json", + "approval_ledger": "reports/adaptation_approval_ledger.json", + }, + }) + + +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 render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Adaptation Regression Report", + "", + f"- generated_at: `{report['generated_at']}`", + f"- apply_supported: `{str(summary['apply_supported']).lower()}`", + f"- attempts: `{summary['attempt_count']}`", + f"- applied: `{summary['applied_count']}`", + f"- dry runs: `{summary['dry_run_count']}`", + f"- rollbacks: `{summary.get('rollback_count', 0)}`", + f"- failures: `{summary['failure_count']}`", + "", + "This report proves the adaptive apply harness behavior. It does not count proposals as applied changes.", + "", + ] + for attempt in report.get("attempts", []): + lines.extend( + [ + f"## {attempt['proposal_id']}", + "", + f"- mode: `{attempt['mode']}`", + f"- status: `{attempt['status']}`", + f"- patch: `{attempt['patch']}`", + f"- patch sha256: `{attempt['patch_sha256']}`", + f"- targets: `{', '.join(attempt['target_files'])}`", + f"- rollback: {attempt['rollback']['plan']}", + ] + ) + if attempt.get("regression_runs"): + lines.append("- regression:") + lines.extend(f" - `{item['command']}`: `{str(item['ok']).lower()}`" for item in attempt["regression_runs"]) + lines.append("") + if report.get("failures"): + lines.extend(["## Failures", ""]) + lines.extend(f"- {failure}" for failure in report["failures"]) + draft = report.get("approval_draft") + if isinstance(draft, dict): + lines.extend( + [ + "", + "## Approval Draft", + "", + f"- proposal: `{draft.get('proposal_id', '')}`", + f"- decision: `{draft.get('decision', '')}`", + f"- patch sha256: `{draft.get('patch_sha256', '')}`", + f"- targets: `{', '.join(draft.get('target_files', []))}`", + "", + "A human reviewer must set `decision` to `approved` and fill reviewer, reason, and approval date before apply.", + ] + ) + return "\n".join(lines).rstrip() + "\n" + + +def build_report(args: argparse.Namespace) -> dict[str, Any]: + skill_dir = Path(args.skill_dir).resolve() + generated_at = args.generated_at or utc_now() + ledger_path = resolve_path(skill_dir, args.approval_ledger) + output_json = resolve_path(skill_dir, args.output_json) + output_md = resolve_path(skill_dir, args.output_md) + if args.write_template: + ledger = empty_approval_ledger(generated_at) + report = empty_regression_report(skill_dir, generated_at) + write_json(ledger_path, ledger) + write_json(output_json, report) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + failures: list[str] = [] + proposals_path = resolve_path(skill_dir, args.proposals_json) + patch_path = resolve_path(skill_dir, args.patch_file) if args.patch_file else Path("") + proposals = load_json(proposals_path) + ledger = load_json(ledger_path) + if not proposals: + failures.append(f"Proposal report missing or invalid: {display_path(proposals_path, skill_dir)}") + if args.prepare_approval and not ledger and not ledger_path.exists(): + ledger = empty_approval_ledger(generated_at) + if not args.prepare_approval and not ledger: + failures.append(f"Approval ledger missing or invalid: {display_path(ledger_path, skill_dir)}") + if args.prepare_approval and ledger_path.exists() and not ledger: + failures.append(f"Approval ledger exists but is invalid: {display_path(ledger_path, skill_dir)}") + if not args.proposal_id: + failures.append("--proposal-id is required unless --write-template is used.") + if not args.patch_file or not patch_path.exists(): + failures.append("--patch-file must point to an existing unified diff.") + + today = parse_date(str(args.today or date.today().isoformat())) + if today is None: + failures.append("--today must be an ISO date when provided.") + + proposal = find_proposal(proposals, args.proposal_id) if not failures else {} + if not failures and not proposal: + failures.append(f"Proposal id is not present in proposal report: {args.proposal_id}") + + patch_sha = sha256_file(patch_path) if patch_path.exists() else "" + target_files: list[str] = [] + observed_target_file_sha256: dict[str, str] = {} + if not failures: + try: + target_files = patch_target_files(patch_path.read_text(encoding="utf-8", errors="replace")) + except ValueError as exc: + failures.append(str(exc)) + + if args.prepare_approval: + if not failures: + proposal_targets = set(str(item) for item in proposal.get("target_files", [])) + patch_targets = set(target_files) + if not patch_targets: + failures.append("Patch does not declare any target files.") + if not patch_targets <= proposal_targets: + failures.append("Patch touches files outside proposal target_files.") + check = git_apply_check(skill_dir, patch_path) if not failures else {"ok": False, "returncode": None, "stdout": "", "stderr": ""} + if not failures and not check["ok"]: + failures.append(f"git apply --check failed: {check['stderr'].strip()}") + draft: dict[str, Any] = {} + if not failures: + observed_target_file_sha256 = target_file_sha256(skill_dir, target_files) + draft = { + "proposal_id": args.proposal_id, + "decision": "pending-review", + "reviewer": "", + "reason": "", + "approved_at": "", + "expires_at": "", + "created_at": generated_at, + "patch": display_path(patch_path, skill_dir), + "patch_sha256": patch_sha, + "target_files": target_files, + "target_file_sha256": observed_target_file_sha256, + "verification_commands": [ + str(command) for command in proposal.get("verification_commands", []) if command + ], + "rollback_plan": proposal.get("rollback_plan", f"git apply -R {display_path(patch_path, skill_dir)}"), + } + upsert_ledger_entry(ledger, draft) + write_json(ledger_path, ledger) + report = decorate_regression_report({ + "schema_version": "1.0", + "ok": not failures, + "generated_at": generated_at, + "skill_dir": display_path(skill_dir, skill_dir), + "summary": { + "apply_supported": True, + "attempt_count": 0, + "approval_draft_count": 1 if draft else 0, + "applied_count": 0, + "dry_run_count": 0, + "rollback_count": 0, + "regression_run_count": 0, + "regression_pass_count": 0, + "failure_count": len(failures), + }, + "apply_contract": dict(APPLY_CONTRACT), + "approval_draft": draft, + "attempts": [], + "failures": failures, + "artifacts": { + "json": display_path(output_json, skill_dir), + "markdown": display_path(output_md, skill_dir), + "approval_ledger": display_path(ledger_path, skill_dir), + }, + }) + write_json(output_json, report) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + approval = find_approval(ledger, args.proposal_id) if not failures else {} + if not failures and not approval: + failures.append(f"Proposal id is not approved in the ledger: {args.proposal_id}") + if approval: + refresh_ledger_summary(ledger) + write_json(ledger_path, ledger) + failures.extend(validate_approval(approval, today or date.today())) + + if not failures: + approved_targets = set(str(item) for item in approval.get("target_files", [])) + proposal_targets = set(str(item) for item in proposal.get("target_files", [])) + patch_targets = set(target_files) + if patch_sha != approval.get("patch_sha256"): + failures.append("Patch sha256 does not match approval ledger.") + if not patch_targets: + failures.append("Patch does not declare any target files.") + if not patch_targets <= approved_targets: + failures.append("Patch touches files outside approval target_files.") + if not patch_targets <= proposal_targets: + failures.append("Patch touches files outside proposal target_files.") + if not failures: + baseline_failures, observed_target_file_sha256 = validate_target_file_sha256( + skill_dir, + target_files, + approval.get("target_file_sha256", {}), + ) + failures.extend(baseline_failures) + + check = git_apply_check(skill_dir, patch_path) if not failures else {"ok": False, "returncode": None, "stdout": "", "stderr": ""} + if not failures and not check["ok"]: + failures.append(f"git apply --check failed: {check['stderr'].strip()}") + + mode = "apply" if args.apply else "dry-run" + applied = False + apply_result = {"ok": False, "returncode": None, "stdout": "", "stderr": "not requested"} + if not failures and args.apply: + apply_result = git_apply(skill_dir, patch_path) + applied = bool(apply_result["ok"]) + if not applied: + failures.append(f"git apply failed: {apply_result['stderr'].strip()}") + + regression_runs: list[dict[str, Any]] = [] + if not failures and args.run_verification: + for command in approval.get("verification_commands", []): + regression_runs.append(run_command(str(command), skill_dir)) + if any(not item["ok"] for item in regression_runs): + failures.append("One or more regression commands failed or were not allowed.") + + rollback_result = {"ok": None, "returncode": None, "stdout": "", "stderr": "not needed"} + rolled_back = False + if applied and failures and args.rollback_on_failure: + rollback_result = git_apply_reverse(skill_dir, patch_path) + rolled_back = bool(rollback_result["ok"]) + if not rolled_back: + failures.append(f"Automatic rollback failed: {rollback_result['stderr'].strip()}") + + attempt = { + "proposal_id": args.proposal_id or "", + "mode": mode, + "status": "failed-rolled-back" if rolled_back else ("failed" if failures else ("applied" if applied else "dry-run-pass")), + "patch": display_path(patch_path, skill_dir) if args.patch_file else "", + "patch_sha256": patch_sha, + "target_files": target_files, + "approval": { + "reviewer": approval.get("reviewer", ""), + "approved_at": approval.get("approved_at", ""), + "expires_at": approval.get("expires_at", ""), + }, + "target_file_sha256": { + "expected": { + target: str(approval.get("target_file_sha256", {}).get(target, "")) + for target in target_files + }, + "observed": observed_target_file_sha256, + }, + "git_apply_check": check, + "git_apply": apply_result, + "regression_runs": regression_runs, + "rollback_result": rollback_result, + "rollback": { + "plan": approval.get("rollback_plan", ""), + "command": f"git apply -R {display_path(patch_path, skill_dir)}" if args.patch_file else "", + }, + } + report = decorate_regression_report({ + "schema_version": "1.0", + "ok": not failures, + "generated_at": generated_at, + "skill_dir": display_path(skill_dir, skill_dir), + "summary": { + "apply_supported": True, + "attempt_count": 1, + "approval_draft_count": 0, + "applied_count": 1 if applied and not rolled_back else 0, + "dry_run_count": 0 if args.apply else 1, + "rollback_count": 1 if rolled_back else 0, + "regression_run_count": len(regression_runs), + "regression_pass_count": sum(1 for item in regression_runs if item["ok"]), + "failure_count": len(failures), + }, + "apply_contract": dict(APPLY_CONTRACT), + "attempts": [attempt], + "failures": failures, + "artifacts": { + "json": display_path(output_json, skill_dir), + "markdown": display_path(output_md, skill_dir), + "approval_ledger": display_path(ledger_path, skill_dir), + }, + }) + write_json(output_json, report) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Apply an approved adaptation patch with review and regression evidence.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--proposal-id") + parser.add_argument("--patch-file") + parser.add_argument("--proposals-json", default="reports/adaptation_proposals.json") + parser.add_argument("--approval-ledger", default="reports/adaptation_approval_ledger.json") + parser.add_argument("--output-json", default="reports/adaptation_regression_report.json") + parser.add_argument("--output-md", default="reports/adaptation_regression_report.md") + parser.add_argument("--generated-at") + parser.add_argument("--today") + parser.add_argument("--write-template", action="store_true") + parser.add_argument( + "--prepare-approval", + action="store_true", + help="Create or update a pending approval ledger entry with patch and target baseline hashes.", + ) + parser.add_argument("--apply", action="store_true", help="Write the patch after every approval and allowlist check passes.") + parser.add_argument("--run-verification", action="store_true") + parser.add_argument( + "--no-rollback-on-failure", + dest="rollback_on_failure", + action="store_false", + help="Leave an applied patch in place if verification fails. Default is to reverse the patch.", + ) + parser.set_defaults(rollback_on_failure=True) + args = parser.parse_args() + + report = build_report(args) + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/benchmark_release_lock.py b/scripts/benchmark_release_lock.py new file mode 100644 index 0000000..cd8157e --- /dev/null +++ b/scripts/benchmark_release_lock.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Git source/generated dirty classification for benchmark release locks.""" + +import subprocess +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_benchmark_reproducibility.py to keep release-lock git status classification out of the report renderer." + + +GENERATED_DIRTY_PREFIXES = ( + "dist/", + "registry/index.json", + "registry/packages/", + "reports/", + "skill_atlas/", + "skill-ir/examples/", +) + + +def status_path(line: str) -> str: + path = line[3:].strip() if len(line) > 3 else line.strip() + if " -> " in path: + path = path.rsplit(" -> ", 1)[-1].strip() + return path + + +def is_generated_dirty_path(path: str) -> bool: + return any(path == prefix.rstrip("/") or path.startswith(prefix) for prefix in GENERATED_DIRTY_PREFIXES) + + +def git_status(skill_dir: Path) -> dict[str, Any]: + try: + proc = subprocess.run( + ["git", "status", "--short", "--untracked-files=all"], + cwd=skill_dir, + capture_output=True, + text=True, + check=True, + ) + except (OSError, subprocess.CalledProcessError): + return { + "available": False, + "dirty": None, + "changed_file_count": None, + "source_dirty": None, + "source_changed_file_count": None, + "generated_dirty": None, + "generated_changed_file_count": None, + } + lines = [line for line in proc.stdout.splitlines() if line.strip()] + paths = [status_path(line) for line in lines] + generated_lines = [line for line, path in zip(lines, paths) if is_generated_dirty_path(path)] + source_lines = [line for line, path in zip(lines, paths) if not is_generated_dirty_path(path)] + return { + "available": True, + "dirty": bool(lines), + "changed_file_count": len(lines), + "generated_dirty": bool(generated_lines), + "generated_changed_file_count": len(generated_lines), + "source_dirty": bool(source_lines), + "source_changed_file_count": len(source_lines), + "sample": lines[:12], + "source_sample": source_lines[:12], + "generated_sample": generated_lines[:12], + "generated_dirty_prefixes": list(GENERATED_DIRTY_PREFIXES), + "scope": "generation-time status before this report is written", + } + + +def release_lock_status(status: dict[str, Any], commit: str) -> dict[str, Any]: + available = status.get("available") is True + source_clean = status.get("source_dirty") is False + known_commit = bool(commit and commit != "unknown") + ready = available and source_clean and known_commit + reasons = [] + if not available: + reasons.append("git status unavailable") + if not known_commit: + reasons.append("git commit unavailable") + if status.get("source_dirty") is True: + reasons.append("source files were dirty at generation time") + if status.get("source_dirty") is None: + reasons.append("working tree cleanliness unknown") + if status.get("generated_dirty") is True and status.get("source_dirty") is False: + reasons.append("only generated evidence artifacts were dirty at generation time") + if not reasons: + reasons.append("clean source tree at generation-time HEAD") + return { + "ready": ready, + "commit": commit, + "status_scope": status.get("scope", "generation-time status"), + "source_changed_file_count": status.get("source_changed_file_count"), + "generated_changed_file_count": status.get("generated_changed_file_count"), + "reason": "; ".join(reasons), + } diff --git a/scripts/build_confusion_matrix.py b/scripts/build_confusion_matrix.py new file mode 100644 index 0000000..5c76960 --- /dev/null +++ b/scripts/build_confusion_matrix.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from trigger_eval import extract_description, load_json, load_semantic_config, score_prompt_semantic + + +ROOT = Path(__file__).resolve().parent.parent + + +def load_routes(config_path: Path) -> tuple[list[dict], dict]: + payload = load_json(config_path) + routes = [] + for item in payload["routes"]: + description = extract_description((ROOT / item["description_file"]).read_text(encoding="utf-8")).strip() + routes.append( + { + "name": item["name"], + "description": description, + "semantic_config": load_semantic_config(ROOT / item["semantic_config"]), + "threshold": item["threshold"], + } + ) + return routes, payload + + +def display_value(value: object) -> str: + return "-" if value is None else str(value) + + +def build_scorecard(routes: list[dict], payload: dict) -> dict: + margin_warning_threshold = payload.get("margin_warning_threshold", 0.08) + cases = [] + route_names = [route["name"] for route in routes] + ["no_route"] + confusion = {expected: {predicted: 0 for predicted in route_names} for expected in route_names} + + route_stats = { + name: { + "expected_count": 0, + "predicted_count": 0, + "correct_count": 0, + "precision": None, + "recall": None, + "average_margin": None, + } + for name in route_names + } + margins_by_route: dict[str, list[float]] = {name: [] for name in route_names} + + for case in payload["cases"]: + prompt = case["text"] + scored = [] + for route in routes: + score, detail = score_prompt_semantic(route["description"], prompt, route["semantic_config"]) + passed = score >= route["threshold"] + scored.append( + { + "name": route["name"], + "score": round(score, 3), + "passed_threshold": passed, + "threshold": route["threshold"], + "matched_desired_concepts": detail["matched_desired_concepts"], + "matched_negative_concepts": detail["matched_negative_concepts"], + } + ) + ranked = sorted(scored, key=lambda item: item["score"], reverse=True) + passed = [item for item in ranked if item["passed_threshold"]] + predicted = passed[0]["name"] if passed else "no_route" + winning_score = passed[0]["score"] if passed else 0.0 + second_score = passed[1]["score"] if len(passed) > 1 else 0.0 + margin = round(winning_score - second_score, 3) if predicted != "no_route" else None + ambiguous = predicted != "no_route" and margin is not None and margin < margin_warning_threshold + correct = predicted == case["expected_route"] + + case_record = { + "family": case["family"], + "prompt": prompt, + "expected_route": case["expected_route"], + "predicted_route": predicted, + "correct": correct, + "margin": margin, + "ambiguous": ambiguous, + "ranked_routes": ranked, + } + cases.append(case_record) + + confusion[case["expected_route"]][predicted] += 1 + route_stats[case["expected_route"]]["expected_count"] += 1 + route_stats[predicted]["predicted_count"] += 1 + if correct: + route_stats[predicted]["correct_count"] += 1 + if margin is not None: + margins_by_route[predicted].append(margin) + + for route_name, stats in route_stats.items(): + predicted_count = stats["predicted_count"] + expected_count = stats["expected_count"] + correct_count = stats["correct_count"] + stats["precision"] = round(correct_count / predicted_count, 3) if predicted_count else None + stats["recall"] = round(correct_count / expected_count, 3) if expected_count else None + stats["average_margin"] = ( + round(sum(margins_by_route[route_name]) / len(margins_by_route[route_name]), 3) + if margins_by_route[route_name] + else None + ) + + correct_cases = sum(1 for case in cases if case["correct"]) + ambiguous_cases = [case for case in cases if case["ambiguous"]] + misroutes = [case for case in cases if not case["correct"]] + return { + "summary": { + "total_cases": len(cases), + "correct_cases": correct_cases, + "accuracy": round(correct_cases / len(cases), 3) if cases else None, + "ambiguous_case_count": len(ambiguous_cases), + "margin_warning_threshold": margin_warning_threshold, + "misroute_count": len(misroutes), + "no_route_accuracy": route_stats["no_route"]["recall"], + }, + "route_stats": route_stats, + "confusion_matrix": confusion, + "ambiguous_cases": ambiguous_cases, + "misroutes": misroutes, + "cases": cases, + } + + +def build_history_snapshot(scorecard: dict, args: argparse.Namespace) -> dict: + return { + "snapshot_id": args.snapshot_id, + "date": args.snapshot_date, + "commit": args.snapshot_commit, + "label": args.snapshot_label, + "trigger_summary": { + "total_cases": scorecard["summary"]["total_cases"], + "family_count": len({case["family"] for case in scorecard["cases"]}), + "false_positives": 0, + "false_negatives": scorecard["summary"]["misroute_count"], + "average_precision": scorecard["summary"]["accuracy"], + "average_recall": scorecard["summary"]["accuracy"], + }, + "route_summary": { + "accuracy": scorecard["summary"]["accuracy"], + "ambiguous_case_count": scorecard["summary"]["ambiguous_case_count"], + "misroute_count": scorecard["summary"]["misroute_count"], + "no_route_accuracy": scorecard["summary"]["no_route_accuracy"], + }, + "quality_gates": { + "route_confusion_checks": True, + "promotion_policy_present": True, + }, + "governance_score": None, + "governed_examples": 1, + "notes": [ + "added route confusion matrix for sibling skills and no-route cases", + "published route scorecard and iteration ledger foundation", + ], + } + + +def render_markdown(scorecard: dict) -> str: + lines = [ + "# Route Scorecard", + "", + f"- total cases: `{scorecard['summary']['total_cases']}`", + f"- accuracy: `{scorecard['summary']['accuracy']}`", + f"- ambiguous cases: `{scorecard['summary']['ambiguous_case_count']}`", + f"- no-route accuracy: `{display_value(scorecard['summary']['no_route_accuracy'])}`", + "", + "## Route Metrics", + "", + "| Route | Expected | Predicted | Precision | Recall | Avg Margin |", + "| --- | ---: | ---: | ---: | ---: | ---: |", + ] + for route_name, stats in scorecard["route_stats"].items(): + lines.append( + f"| `{route_name}` | {stats['expected_count']} | {stats['predicted_count']} | {display_value(stats['precision'])} | {display_value(stats['recall'])} | {display_value(stats['average_margin'])} |" + ) + + route_names = list(scorecard["confusion_matrix"].keys()) + lines.extend( + [ + "", + "## Confusion Matrix", + "", + "| Expected \\ Predicted | " + " | ".join(f"`{name}`" for name in route_names) + " |", + "| --- | " + " | ".join("---:" for _ in route_names) + " |", + ] + ) + for expected in route_names: + row = [str(scorecard["confusion_matrix"][expected][predicted]) for predicted in route_names] + lines.append(f"| `{expected}` | " + " | ".join(row) + " |") + + lines.extend( + [ + "", + "## Ambiguous Cases", + "", + "| Family | Expected | Predicted | Margin |", + "| --- | --- | --- | ---: |", + ] + ) + if scorecard["ambiguous_cases"]: + for case in scorecard["ambiguous_cases"]: + lines.append( + f"| `{case['family']}` | `{case['expected_route']}` | `{case['predicted_route']}` | {display_value(case['margin'])} |" + ) + else: + lines.append("| - | - | - | - |") + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Build a route confusion matrix across skills.") + parser.add_argument("--cases", default="evals/confusion/route_cases.json") + parser.add_argument("--output-json", default="reports/route_scorecard.json") + parser.add_argument("--output-md", default="reports/route_scorecard.md") + parser.add_argument("--history-snapshot-output") + parser.add_argument("--snapshot-date") + parser.add_argument("--snapshot-id", default="route-scorecard-foundation") + parser.add_argument("--snapshot-label", default="Route Scorecard Foundation") + parser.add_argument("--snapshot-commit", default="local-snapshot") + args = parser.parse_args() + + cases_path = ROOT / args.cases if not Path(args.cases).is_absolute() else Path(args.cases) + routes, payload = load_routes(cases_path) + scorecard = build_scorecard(routes, payload) + + output_json = ROOT / args.output_json if not Path(args.output_json).is_absolute() else Path(args.output_json) + output_md = ROOT / args.output_md if not Path(args.output_md).is_absolute() else Path(args.output_md) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(scorecard, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(scorecard), encoding="utf-8") + + if args.history_snapshot_output: + snapshot_path = ROOT / args.history_snapshot_output if not Path(args.history_snapshot_output).is_absolute() else Path(args.history_snapshot_output) + snapshot_path.parent.mkdir(parents=True, exist_ok=True) + snapshot = build_history_snapshot(scorecard, args) + snapshot_path.write_text(json.dumps(snapshot, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + print(json.dumps(scorecard, ensure_ascii=False, indent=2)) + if scorecard["summary"]["misroute_count"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/build_skill_atlas.py b/scripts/build_skill_atlas.py new file mode 100644 index 0000000..049d0c0 --- /dev/null +++ b/scripts/build_skill_atlas.py @@ -0,0 +1,586 @@ +#!/usr/bin/env python3 +import argparse +import csv +import json +import re +from collections import Counter, defaultdict +from datetime import date, datetime +from io import StringIO +from pathlib import Path +from typing import Any + +from build_skill_atlas_opportunities import no_route_opportunities +from build_skill_atlas_layout import render_html + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +ROOT = Path(__file__).resolve().parent.parent +IGNORE_PARTS = { + ".git", + "__pycache__", + "dist", + ".previews", + "node_modules", + ".venv", + "venv", +} +STOPWORDS = { + "a", + "an", + "and", + "the", + "to", + "for", + "from", + "with", + "into", + "skill", + "skills", + "agent", + "reusable", + "use", + "when", + "create", + "turn", +} +CADENCE_DAYS = { + "monthly": 31, + "quarterly": 100, + "semiannual": 200, + "annual": 370, + "per-release": 120, +} +DEFAULT_SCOPE = { + "scope": "release", + "actionable": True, + "scope_reason": "default release-actionable skill", +} +TELEMETRY_REQUIRED_MATURITIES = {"production", "library", "governed"} + + +def parse_frontmatter(path: Path) -> tuple[dict[str, Any], str]: + text = path.read_text(encoding="utf-8", errors="replace") + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + payload = {} + for line in frontmatter_text.splitlines(): + if ":" in line: + key, value = line.split(":", 1) + payload[key.strip()] = value.strip().strip('"') + return payload, body + + +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_scope_policy(workspace_root: Path) -> dict[str, Any]: + path = workspace_root / "skill_atlas" / "policy.json" + if not path.exists(): + return {"present": False, "path": safe_rel(workspace_root, path), "rules": []} + payload = load_json(path) + rules = payload.get("scope_rules", []) + return { + "present": True, + "path": safe_rel(workspace_root, path), + "schema_version": str(payload.get("schema_version", "")), + "rules": rules if isinstance(rules, list) else [], + } + + +def should_skip(path: Path, root: Path) -> bool: + try: + rel = path.relative_to(root) + except ValueError: + return True + if any(part in IGNORE_PARTS for part in rel.parts): + return True + return len(rel.parts) >= 2 and rel.parts[0] == "tests" and rel.parts[1].startswith("tmp") + + +def find_skill_dirs(workspace_root: Path) -> list[Path]: + workspace_root = workspace_root.resolve() + skill_dirs = [] + for skill_md in sorted(workspace_root.rglob("SKILL.md")): + if should_skip(skill_md, workspace_root): + continue + skill_dirs.append(skill_md.parent) + return skill_dirs + + +def tokens(text: str) -> set[str]: + raw = re.findall(r"[a-zA-Z0-9_\-\u4e00-\u9fff]{2,}", text.casefold()) + return {item for item in raw if item not in STOPWORDS} + + +def jaccard(left: set[str], right: set[str]) -> float: + if not left or not right: + return 0.0 + return len(left & right) / len(left | right) + + +def safe_rel(root: Path, path: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def path_matches_prefix(rel_path: str, prefix: str) -> bool: + normalized_path = rel_path.strip("/") + normalized_prefix = prefix.strip("/") + if not normalized_prefix: + return False + return normalized_path == normalized_prefix or normalized_path.startswith(normalized_prefix + "/") + + +def scope_for_path(rel_path: str, policy: dict[str, Any]) -> dict[str, Any]: + for rule in policy.get("rules", []): + if not isinstance(rule, dict): + continue + prefix = str(rule.get("path_prefix", "")).strip() + if not prefix or not path_matches_prefix(rel_path, prefix): + continue + return { + "scope": str(rule.get("scope") or "supporting"), + "actionable": bool(rule.get("actionable", False)), + "scope_reason": str(rule.get("reason") or f"matched policy prefix {prefix}"), + } + return dict(DEFAULT_SCOPE) + + +def display_path(path: Path) -> str: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +def resource_names(skill_dir: Path) -> list[str]: + names = [] + for folder in ("scripts", "references", "assets", "templates"): + target = skill_dir / folder + if not target.exists(): + continue + for path in sorted(target.rglob("*")): + rel = path.relative_to(skill_dir) + if any(part in IGNORE_PARTS for part in rel.parts): + continue + if path.suffix in {".pyc", ".pyo"}: + continue + if path.is_file() and not path.is_symlink(): + names.append(f"{folder}/{path.name}") + return names + + +def collect_skill(workspace_root: Path, skill_dir: Path, policy: dict[str, Any]) -> dict[str, Any]: + frontmatter, _ = parse_frontmatter(skill_dir / "SKILL.md") + manifest = load_json(skill_dir / "manifest.json") + name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name) + description = str(frontmatter.get("description") or "") + targets = manifest.get("target_platforms", []) + rel_path = safe_rel(workspace_root, skill_dir) + scope = scope_for_path(rel_path, policy) + return { + "name": name, + "path": rel_path, + "description": description, + "owner": str(manifest.get("owner", "")), + "version": str(manifest.get("version", "")), + "status": str(manifest.get("status", "")), + "maturity": str(manifest.get("maturity_tier", manifest.get("skill_archetype", ""))), + "updated_at": str(manifest.get("updated_at", "")), + "review_cadence": str(manifest.get("review_cadence", "")), + "targets": [str(item) for item in targets] if isinstance(targets, list) else [], + "resources": resource_names(skill_dir), + "token_set": sorted(tokens(description)), + "atlas_scope": scope["scope"], + "actionable": scope["actionable"], + "scope_reason": scope["scope_reason"], + } + + +def load_telemetry_profile(workspace_root: Path, skill_dir: Path, skill: dict[str, Any]) -> tuple[dict[str, Any], list[dict[str, Any]]]: + report_path = skill_dir / "reports" / "adoption_drift_report.json" + rel_report = safe_rel(workspace_root, report_path) + maturity = str(skill.get("maturity", "")).casefold() + report_present = report_path.exists() + telemetry = { + "report_present": report_present, + "report": rel_report, + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0, + } + signals: list[dict[str, Any]] = [] + if not report_present: + if skill.get("actionable") and maturity in TELEMETRY_REQUIRED_MATURITIES: + signals.append( + { + "name": skill["name"], + "path": skill["path"], + "source": rel_report, + "risk_band": "no-data", + "signal_types": ["no telemetry"], + "recommendation": "Render adoption drift evidence or record a small metadata-only sample before release review.", + "actionable": bool(skill.get("actionable")), + "scope": str(skill.get("atlas_scope", "")), + "summary": {"event_count": 0, "adoption_sample_count": 0}, + } + ) + return telemetry, signals + + payload = load_json(report_path) + summary = payload.get("summary", {}) if isinstance(payload.get("summary"), dict) else {} + candidates = payload.get("next_iteration_candidates", []) + candidates = candidates if isinstance(candidates, list) else [] + risk_band = str(summary.get("risk_band") or "unknown") + telemetry.update( + { + "risk_band": risk_band, + "event_count": int(summary.get("event_count") or 0), + "adoption_sample_count": int(summary.get("adoption_sample_count") or 0), + "adoption_rate": summary.get("adoption_rate", 0), + "candidate_count": len(candidates), + } + ) + + signal_types: list[str] = [] + if telemetry["event_count"] == 0 and maturity in TELEMETRY_REQUIRED_MATURITIES: + signal_types.append("no telemetry") + if int(summary.get("missed_trigger_count") or 0): + signal_types.append("missed trigger") + if int(summary.get("wrong_trigger_count") or 0): + signal_types.append("wrong trigger") + if int(summary.get("bad_output_count") or 0): + signal_types.append("bad output") + if int(summary.get("missing_resource_count") or 0): + signal_types.append("missing resource") + if int(summary.get("script_error_count") or 0): + signal_types.append("script error") + if int(summary.get("review_overdue_count") or 0): + signal_types.append("review overdue") + if risk_band in {"medium", "high"} and not signal_types: + signal_types.append("telemetry drift") + + if signal_types: + recommendation = "Convert telemetry drift into eval, trust, or owner-review actions." + for candidate in candidates: + if not isinstance(candidate, dict): + continue + if str(candidate.get("signal", "")) in signal_types: + recommendation = str(candidate.get("recommendation") or recommendation) + break + signals.append( + { + "name": skill["name"], + "path": skill["path"], + "source": rel_report, + "risk_band": risk_band, + "signal_types": signal_types, + "recommendation": recommendation, + "actionable": bool(skill.get("actionable")), + "scope": str(skill.get("atlas_scope", "")), + "summary": { + "event_count": telemetry["event_count"], + "adoption_sample_count": telemetry["adoption_sample_count"], + "adoption_rate": telemetry["adoption_rate"], + "missed_trigger_count": int(summary.get("missed_trigger_count") or 0), + "wrong_trigger_count": int(summary.get("wrong_trigger_count") or 0), + "bad_output_count": int(summary.get("bad_output_count") or 0), + "missing_resource_count": int(summary.get("missing_resource_count") or 0), + "script_error_count": int(summary.get("script_error_count") or 0), + "review_overdue_count": int(summary.get("review_overdue_count") or 0), + }, + } + ) + return telemetry, signals + + +def route_overlap(skills: list[dict[str, Any]], threshold: float) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + rows = [] + collisions = [] + for i, left in enumerate(skills): + for right in skills[i + 1 :]: + score = round(jaccard(set(left["token_set"]), set(right["token_set"])), 3) + status = "collision" if score >= threshold else "clear" + row = { + "skill_a": left["name"], + "skill_b": right["name"], + "path_a": left["path"], + "path_b": right["path"], + "score": score, + "status": status, + "actionable": bool(left.get("actionable") and right.get("actionable")), + "scope_a": str(left.get("atlas_scope", "")), + "scope_b": str(right.get("atlas_scope", "")), + } + rows.append(row) + if status == "collision": + collisions.append(row) + duplicate_names = [ + {"name": name, "paths": [item["path"] for item in skills if item["name"] == name]} + for name, count in Counter(item["name"] for item in skills).items() + if count > 1 + ] + for item in duplicate_names: + collisions.append( + { + "skill_a": item["name"], + "skill_b": item["name"], + "path_a": item["paths"][0], + "path_b": item["paths"][1], + "score": 1.0, + "status": "duplicate-name", + "actionable": all( + skill.get("actionable") + for skill in skills + if skill["name"] == item["name"] and skill["path"] in set(item["paths"][:2]) + ), + "scope_a": next( + (str(skill.get("atlas_scope", "")) for skill in skills if skill["path"] == item["paths"][0]), + "", + ), + "scope_b": next( + (str(skill.get("atlas_scope", "")) for skill in skills if skill["path"] == item["paths"][1]), + "", + ), + } + ) + return rows, collisions + + +def dependency_graph(skills: list[dict[str, Any]]) -> dict[str, Any]: + by_resource: dict[str, list[str]] = defaultdict(list) + for skill in skills: + for resource in skill.get("resources", []): + by_resource[resource].append(skill["name"]) + shared = [ + {"resource": resource, "skills": sorted(names)} + for resource, names in sorted(by_resource.items()) + if len(set(names)) > 1 + ] + return { + "nodes": [{"name": item["name"], "path": item["path"]} for item in skills], + "shared_resources": shared, + } + + +def parse_date(value: str) -> date | None: + if not value: + return None + try: + return datetime.strptime(value[:10], "%Y-%m-%d").date() + except ValueError: + return None + + +def stale_skills(skills: list[dict[str, Any]], today: date) -> list[dict[str, Any]]: + stale = [] + for skill in skills: + updated = parse_date(skill.get("updated_at", "")) + cadence = skill.get("review_cadence") or "" + allowed_days = CADENCE_DAYS.get(cadence, 120) + if not updated: + stale.append( + { + "name": skill["name"], + "path": skill["path"], + "reason": "missing updated_at", + "actionable": bool(skill.get("actionable")), + "scope": str(skill.get("atlas_scope", "")), + } + ) + continue + age = (today - updated).days + if age > allowed_days: + stale.append( + { + "name": skill["name"], + "path": skill["path"], + "reason": f"review overdue by cadence {cadence or 'unspecified'}", + "age_days": age, + "allowed_days": allowed_days, + "actionable": bool(skill.get("actionable")), + "scope": str(skill.get("atlas_scope", "")), + } + ) + return stale + + +def owner_review_gaps(skills: list[dict[str, Any]]) -> list[dict[str, Any]]: + gaps = [] + for skill in skills: + missing = [] + if not skill.get("owner"): + missing.append("owner") + if not skill.get("review_cadence"): + missing.append("review_cadence") + if not skill.get("maturity"): + missing.append("maturity") + if missing: + gaps.append( + { + "name": skill["name"], + "path": skill["path"], + "missing": missing, + "actionable": bool(skill.get("actionable")), + "scope": str(skill.get("atlas_scope", "")), + } + ) + return gaps + + +def write_csv(path: Path, rows: list[dict[str, Any]]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + fields = ["skill_a", "skill_b", "path_a", "path_b", "score", "status", "actionable", "scope_a", "scope_b"] + buffer = StringIO() + writer = csv.DictWriter(buffer, fieldnames=fields, lineterminator="\n") + writer.writeheader() + for row in rows: + writer.writerow({field: row.get(field, "") for field in fields}) + path.write_text(buffer.getvalue(), encoding="utf-8") + + +def build_atlas(workspace_root: Path, output_dir: Path, report_html: Path, report_json: Path, threshold: float, today: date) -> dict[str, Any]: + workspace_root = workspace_root.resolve() + scope_policy = load_scope_policy(workspace_root) + skill_dirs = find_skill_dirs(workspace_root) + skills = [] + drift_signals: list[dict[str, Any]] = [] + telemetry_report_count = 0 + for skill_dir in skill_dirs: + skill = collect_skill(workspace_root, skill_dir, scope_policy) + telemetry, signals = load_telemetry_profile(workspace_root, skill_dir, skill) + skill["telemetry"] = telemetry + telemetry_report_count += 1 if telemetry["report_present"] else 0 + drift_signals.extend(signals) + skills.append(skill) + overlap_rows, collisions = route_overlap(skills, threshold) + graph = dependency_graph(skills) + stale = stale_skills(skills, today) + owner_gaps = owner_review_gaps(skills) + opportunities = no_route_opportunities( + workspace_root, + drift_signals, + should_skip=should_skip, + safe_rel=safe_rel, + ) + actionable_skills = [skill for skill in skills if skill.get("actionable")] + actionable_collisions = [item for item in collisions if item.get("actionable")] + actionable_stale = [item for item in stale if item.get("actionable")] + actionable_owner_gaps = [item for item in owner_gaps if item.get("actionable")] + actionable_drift_signals = [item for item in drift_signals if item.get("actionable")] + summary = { + "skill_count": len(skills), + "actionable_skill_count": len(actionable_skills), + "route_collision_count": len(collisions), + "actionable_route_collision_count": len(actionable_collisions), + "owner_gap_count": len(owner_gaps), + "actionable_owner_gap_count": len(actionable_owner_gaps), + "stale_count": len(stale), + "actionable_stale_count": len(actionable_stale), + "shared_resource_count": len(graph["shared_resources"]), + "no_route_opportunity_count": len(opportunities), + "telemetry_report_count": telemetry_report_count, + "drift_signal_count": len(drift_signals), + "actionable_drift_signal_count": len(actionable_drift_signals), + "non_actionable_issue_count": (len(collisions) - len(actionable_collisions)) + + (len(owner_gaps) - len(actionable_owner_gaps)) + + (len(stale) - len(actionable_stale)) + + (len(drift_signals) - len(actionable_drift_signals)), + } + catalog = { + "workspace_root": display_path(workspace_root), + "generated_at": today.isoformat(), + "skills": skills, + "summary": summary, + } + payload = { + "ok": True, + "workspace_root": display_path(workspace_root), + "summary": summary, + "scope_policy": scope_policy, + "catalog": catalog, + "route_collisions": collisions, + "actionable_route_collisions": actionable_collisions, + "dependency_graph": graph, + "stale_skills": stale, + "actionable_stale_skills": actionable_stale, + "owner_review_gaps": owner_gaps, + "actionable_owner_review_gaps": actionable_owner_gaps, + "drift_signals": drift_signals, + "actionable_drift_signals": actionable_drift_signals, + "no_route_opportunities": opportunities, + "artifacts": { + "catalog": display_path(output_dir / "catalog.json"), + "route_overlap_matrix": display_path(output_dir / "route_overlap_matrix.csv"), + "dependency_graph": display_path(output_dir / "dependency_graph.json"), + "stale_skills": display_path(output_dir / "stale_skills.json"), + "owner_review_gaps": display_path(output_dir / "owner_review_gaps.json"), + "drift_signals": display_path(output_dir / "drift_signals.json"), + "no_route_opportunities": display_path(output_dir / "no_route_opportunities.json"), + "report_json": display_path(report_json), + "report_html": display_path(report_html), + }, + } + output_dir.mkdir(parents=True, exist_ok=True) + (output_dir / "catalog.json").write_text(json.dumps(catalog, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + write_csv(output_dir / "route_overlap_matrix.csv", overlap_rows) + (output_dir / "dependency_graph.json").write_text(json.dumps(graph, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + (output_dir / "stale_skills.json").write_text(json.dumps(stale, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + (output_dir / "owner_review_gaps.json").write_text(json.dumps(owner_gaps, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + (output_dir / "drift_signals.json").write_text(json.dumps(drift_signals, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + (output_dir / "no_route_opportunities.json").write_text(json.dumps(opportunities, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + report_json.parent.mkdir(parents=True, exist_ok=True) + report_html.parent.mkdir(parents=True, exist_ok=True) + report_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + report_html.write_text(render_html(payload), encoding="utf-8") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Build a Skill Atlas for a workspace of agent skills.") + parser.add_argument("--workspace-root", default=".") + parser.add_argument("--output-dir", default=str(ROOT / "skill_atlas")) + parser.add_argument("--report-html", default=str(ROOT / "reports" / "skill_atlas.html")) + parser.add_argument("--report-json", default=str(ROOT / "reports" / "skill_atlas.json")) + parser.add_argument("--overlap-threshold", type=float, default=0.42) + parser.add_argument("--today", default=date.today().isoformat()) + args = parser.parse_args() + today = datetime.strptime(args.today, "%Y-%m-%d").date() + payload = build_atlas( + Path(args.workspace_root).resolve(), + Path(args.output_dir).resolve(), + Path(args.report_html).resolve(), + Path(args.report_json).resolve(), + args.overlap_threshold, + today, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/build_skill_atlas_layout.py b/scripts/build_skill_atlas_layout.py new file mode 100644 index 0000000..d992cfb --- /dev/null +++ b/scripts/build_skill_atlas_layout.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""HTML layout helpers for the Skill Atlas report.""" + +import html +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by build_skill_atlas.py to render the static Skill Atlas HTML report." + + +def render_html(payload: dict[str, Any]) -> str: + summary = payload["summary"] + rows = [] + for skill in payload["catalog"]["skills"][:80]: + rows.append( + "" + f"{html.escape(skill['name'])}" + f"{html.escape(skill['path'])}" + f"{html.escape(skill.get('owner') or 'missing')}" + f"{html.escape(skill.get('maturity') or 'unknown')}" + f"{html.escape(skill.get('review_cadence') or 'missing')}" + f"{html.escape(skill.get('atlas_scope') or 'release')}" + "" + ) + blockers = ( + payload["actionable_route_collisions"][:20] + + payload["actionable_owner_review_gaps"][:20] + + payload["actionable_stale_skills"][:20] + + payload["actionable_drift_signals"][:20] + ) + blocker_items = "".join( + f"
  • {html.escape(item.get('name', item.get('skill_a', 'issue')))} {html.escape(item.get('reason', item.get('status', ', '.join(item.get('missing', item.get('signal_types', []))))))}
  • " + for item in blockers + ) + opportunity_items = "".join( + ( + "
  • " + f"{html.escape(item.get('skill') or item.get('source_type', 'opportunity'))} " + f"{html.escape(item.get('note') or item.get('recommendation') or item.get('signal', 'no-route opportunity'))}" + f"
    {html.escape(item.get('source', ''))}" + "
  • " + ) + for item in payload["no_route_opportunities"][:20] + ) + return f""" + + + + + Skill Atlas + + + +
    +

    Skill Atlas

    +

    Portfolio-level review for route overlap, stale ownership, shared resources, telemetry drift, and no-route opportunities.

    +
    +
    Skills{summary['skill_count']}
    +
    Actionable{summary['actionable_skill_count']}
    +
    Route Collisions{summary['actionable_route_collision_count']}
    +
    Owner Gaps{summary['actionable_owner_gap_count']}
    +
    Stale Skills{summary['actionable_stale_count']}
    +
    Drift Signals{summary['actionable_drift_signal_count']}
    +
    No-Route Opportunities{summary['no_route_opportunity_count']}
    +
    +
    +

    Actionable Issues

    +
      {blocker_items or '
    • No blocking portfolio issues detected.
    • '}
    +
    +
    +

    No-Route Opportunities

    +

    Missed-trigger telemetry and explicit failure cases become candidate routing work without storing raw prompts or outputs.

    +
      {opportunity_items or '
    • No no-route opportunities detected.
    • '}
    +
    +
    +

    Full Portfolio Counts

    +

    All scanned skills remain visible: {summary['route_collision_count']} total route collisions, {summary['owner_gap_count']} total owner gaps, {summary['stale_count']} total stale signals, and {summary['drift_signal_count']} telemetry drift signals.

    +
    +
    +

    Catalog

    + + + {''.join(rows)} +
    NamePathOwnerMaturityReviewScope
    +
    +
    + + +""" diff --git a/scripts/build_skill_atlas_opportunities.py b/scripts/build_skill_atlas_opportunities.py new file mode 100644 index 0000000..d0d2b2e --- /dev/null +++ b/scripts/build_skill_atlas_opportunities.py @@ -0,0 +1,82 @@ +from pathlib import Path +from typing import Any, Callable + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by build_skill_atlas.py to keep no-route opportunity detection out of the atlas CLI." + + +SkipPredicate = Callable[[Path, Path], bool] +RelFormatter = Callable[[Path, Path], str] + + +def failure_case_no_route_opportunities( + workspace_root: Path, + *, + should_skip: SkipPredicate, + safe_rel: RelFormatter, +) -> list[dict[str, Any]]: + opportunities = [] + for path in sorted(workspace_root.rglob("failure-cases.md")): + if should_skip(path, workspace_root): + continue + text = path.read_text(encoding="utf-8", errors="replace") + for line in text.splitlines(): + stripped = line.strip() + if not stripped.startswith("-"): + continue + lowered = stripped.casefold() + if "no_route" in lowered or "no route" in lowered or "missed" in lowered or "under-trigger" in lowered: + opportunities.append( + { + "source_type": "failure-case", + "source": safe_rel(workspace_root, path), + "note": stripped.lstrip("- ").strip(), + "actionable": True, + "privacy_contract": "source note only; raw prompts are not required", + } + ) + return opportunities[:50] + + +def telemetry_no_route_opportunities(drift_signals: list[dict[str, Any]]) -> list[dict[str, Any]]: + opportunities = [] + for signal in drift_signals: + signal_types = {str(item) for item in signal.get("signal_types", [])} + if not {"missed trigger", "under trigger"} & signal_types: + continue + summary = signal.get("summary", {}) if isinstance(signal.get("summary"), dict) else {} + opportunities.append( + { + "source_type": "telemetry", + "source": str(signal.get("source", "")), + "skill": str(signal.get("name", "")), + "path": str(signal.get("path", "")), + "signal": "missed trigger", + "missed_trigger_count": int(summary.get("missed_trigger_count") or 0), + "recommendation": str( + signal.get("recommendation") + or "Add missed prompts to trigger eval and evaluate whether a new skill route is needed." + ), + "actionable": bool(signal.get("actionable")), + "scope": str(signal.get("scope", "")), + "privacy_contract": "metadata-only telemetry; no raw prompt, output, transcript, or note is stored", + } + ) + return opportunities + + +def no_route_opportunities( + workspace_root: Path, + drift_signals: list[dict[str, Any]], + *, + should_skip: SkipPredicate, + safe_rel: RelFormatter, +) -> list[dict[str, Any]]: + opportunities = failure_case_no_route_opportunities( + workspace_root, + should_skip=should_skip, + safe_rel=safe_rel, + ) + opportunities.extend(telemetry_no_route_opportunities(drift_signals)) + return opportunities[:80] diff --git a/scripts/check_update.py b/scripts/check_update.py new file mode 100644 index 0000000..d54b518 --- /dev/null +++ b/scripts/check_update.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import re +import sys +from datetime import date +from pathlib import Path +from urllib.error import HTTPError, URLError +from urllib.parse import urlparse +from urllib.request import Request, urlopen + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_VERSION_URL = "https://raw.githubusercontent.com/yaojingang/yao-meta-skill/main/VERSION" +DEFAULT_MANIFEST_URL = "https://raw.githubusercontent.com/yaojingang/yao-meta-skill/main/manifest.json" +CACHE_DIR = ROOT / ".yao" +CACHE_PATH = CACHE_DIR / "update-check.json" +ALLOWED_UPDATE_HOST = "raw.githubusercontent.com" +ALLOWED_UPDATE_PATH_PREFIX = "/yaojingang/yao-meta-skill/" + + +def load_local_version(root: Path) -> str: + version_file = root / "VERSION" + if version_file.exists(): + value = version_file.read_text(encoding="utf-8").strip() + if value: + return value + manifest_path = root / "manifest.json" + if manifest_path.exists(): + payload = json.loads(manifest_path.read_text(encoding="utf-8")) + version = str(payload.get("version", "")).strip() + if version: + return version + return "0.0.0" + + +def normalize_version(value: str) -> tuple[int, ...]: + parts = re.findall(r"\d+", value) + if not parts: + return (0,) + return tuple(int(part) for part in parts) + + +def is_newer(remote: str, local: str) -> bool: + remote_parts = normalize_version(remote) + local_parts = normalize_version(local) + length = max(len(remote_parts), len(local_parts)) + remote_parts = remote_parts + (0,) * (length - len(remote_parts)) + local_parts = local_parts + (0,) * (length - len(local_parts)) + return remote_parts > local_parts + + +def fetch_text(url: str, timeout: float) -> str: + request = Request(url, headers={"User-Agent": "yao-meta-skill-update-check"}) + with urlopen(request, timeout=timeout) as response: + return response.read().decode("utf-8").strip() + + +def validate_update_url(url: str, allow_custom: bool) -> None: + parsed = urlparse(url) + if parsed.scheme != "https": + raise ValueError(f"Update URL scheme is not allowed: {parsed.scheme or 'missing'}") + is_default_source = ( + parsed.netloc == ALLOWED_UPDATE_HOST + and parsed.path.startswith(ALLOWED_UPDATE_PATH_PREFIX) + ) + if not is_default_source and not allow_custom: + raise ValueError("Custom update URLs require --allow-custom-update-url.") + + +def validate_update_urls(version_url: str, manifest_url: str, allow_custom: bool) -> None: + validate_update_url(version_url, allow_custom) + validate_update_url(manifest_url, allow_custom) + + +def fetch_remote_version(version_url: str, manifest_url: str, timeout: float) -> tuple[str, str]: + try: + value = fetch_text(version_url, timeout) + if value: + return value.splitlines()[0].strip(), version_url + except (HTTPError, URLError, TimeoutError, OSError): + pass + manifest_text = fetch_text(manifest_url, timeout) + payload = json.loads(manifest_text) + version = str(payload.get("version", "")).strip() + if not version: + raise ValueError("Remote manifest does not contain a version.") + return version, manifest_url + + +def read_cache(path: Path) -> dict: + if not path.exists(): + return {} + try: + payload = json.loads(path.read_text(encoding="utf-8")) + return payload if isinstance(payload, dict) else {} + except (json.JSONDecodeError, OSError): + return {} + + +def write_cache(path: Path, payload: dict) -> 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 build_result( + local_version: str, + remote_version: str | None, + source: str | None, + checked: bool, + skipped: bool = False, + error: str | None = None, +) -> dict: + update_available = bool(remote_version and is_newer(remote_version, local_version)) + return { + "ok": error is None, + "checked": checked, + "skipped": skipped, + "local_version": local_version, + "remote_version": remote_version, + "update_available": update_available, + "source": source, + "install_hint": "npx skills add yaojingang/yao-meta-skill" if update_available else "", + "error": error, + } + + +def check_update( + root: Path, + cache_path: Path, + version_url: str, + manifest_url: str, + timeout: float, + max_age_days: int, + force: bool, + no_cache: bool, + allow_custom_url: bool = False, +) -> dict: + local_version = load_local_version(root) + today = str(date.today()) + if not force and not no_cache: + cache = read_cache(cache_path) + if cache.get("checked_at") == today and cache.get("local_version") == local_version: + return { + **build_result( + local_version=local_version, + remote_version=cache.get("remote_version"), + source=cache.get("source"), + checked=False, + skipped=True, + error=cache.get("error"), + ), + "cached": True, + } + try: + validate_update_urls(version_url, manifest_url, allow_custom_url) + remote_version, source = fetch_remote_version(version_url, manifest_url, timeout) + result = build_result(local_version, remote_version, source, checked=True) + except Exception as exc: # noqa: BLE001 - update checks should never break authoring. + result = build_result(local_version, None, None, checked=True, error=str(exc)) + if not no_cache: + write_cache( + cache_path, + { + "checked_at": today, + "local_version": local_version, + "remote_version": result.get("remote_version"), + "source": result.get("source"), + "error": result.get("error"), + }, + ) + result["cached"] = False + result["max_age_days"] = max_age_days + return result + + +def main() -> None: + parser = argparse.ArgumentParser(description="Check whether yao-meta-skill has a newer GitHub version.") + parser.add_argument("--root", default=str(ROOT)) + parser.add_argument("--cache-path", default=str(CACHE_PATH)) + parser.add_argument("--version-url", default=os.environ.get("YAO_UPDATE_VERSION_URL", DEFAULT_VERSION_URL)) + parser.add_argument("--manifest-url", default=os.environ.get("YAO_UPDATE_MANIFEST_URL", DEFAULT_MANIFEST_URL)) + parser.add_argument("--timeout", type=float, default=3.0) + parser.add_argument("--max-age-days", type=int, default=1) + parser.add_argument("--force", action="store_true") + parser.add_argument("--no-cache", action="store_true") + parser.add_argument( + "--allow-custom-update-url", + action="store_true", + default=os.environ.get("YAO_ALLOW_CUSTOM_UPDATE_URL") == "1", + help="Allow custom HTTPS update URLs. file:// and non-HTTPS schemes are always blocked.", + ) + args = parser.parse_args() + result = check_update( + root=Path(args.root).resolve(), + cache_path=Path(args.cache_path).resolve(), + version_url=args.version_url, + manifest_url=args.manifest_url, + timeout=args.timeout, + max_age_days=args.max_age_days, + force=args.force, + no_cache=args.no_cache, + allow_custom_url=args.allow_custom_update_url, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + sys.exit(0 if result["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/ci_test.py b/scripts/ci_test.py new file mode 100644 index 0000000..e86d06e --- /dev/null +++ b/scripts/ci_test.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +import argparse +import os +import subprocess +import sys +import tempfile +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SAFE_ENV_KEYS = ( + "HOME", + "LANG", + "LC_ALL", + "PATH", + "PYTHONPATH", + "PYTHONIOENCODING", + "TEMP", + "TERM", + "TMP", + "TMPDIR", + "TZ", +) +DEFAULT_TARGETS = [ + "eval", + "eval-suite", + "route-scorecard", + "route-confusion-check", + "description-optimization", + "description-optimization-check", + "promotion-check", + "python-compat-check", + "architecture-maintainability-check", + "yao-cli-check", + "yao-cli-world-class-check", + "operator-ux-check", + "skill-overview-check", + "skill-interpretation-check", + "skill-report-metrics-check", + "skill-report-charts-check", + "html-rendering-check", + "skill-ir-check", + "compiler-check", + "output-eval-check", + "output-execution-check", + "output-review-kit-check", + "output-review-adjudication-check", + "runtime-conformance-check", + "runtime-permission-check", + "trust-check", + "skill-atlas-check", + "registry-audit-check", + "package-verify-check", + "install-simulation-check", + "upgrade-check", + "review-viewer-check", + "review-studio-check", + "skill-os2-audit-check", + "skill-os2-coverage-check", + "world-class-evidence-check", + "world-class-ledger-check", + "world-class-intake-check", + "world-class-submission-kit-check", + "world-class-preflight-check", + "world-class-submission-review-check", + "world-class-runbook-check", + "world-class-claim-guard-check", + "benchmark-reproducibility-check", + "evidence-consistency-check", + "feedback-check", + "adaptation-safety-check", + "skillops-opportunity-check", + "daily-skillops-check", + "weekly-curator-check", + "adoption-drift-check", + "telemetry-import-check", + "telemetry-emit-check", + "telemetry-hooks-check", + "telemetry-native-host-check", + "review-waivers-check", + "review-annotations-check", + "baseline-compare-check", + "reference-scan-check", + "github-benchmark-scan-check", + "reference-synthesis-check", + "output-risk-profile-check", + "artifact-design-profile-check", + "prompt-quality-profile-check", + "system-model-check", + "iteration-directions-check", + "description-drift-history", + "iteration-ledger", + "regression-history", + "context-reports", + "portability-report", + "portability-check", + "failure-regression-check", + "package-check", + "package-failure-check", + "security-boundary-check", + "local-install-sync-check", + "snapshot-check", + "validate", + "lint", + "governance-check", + "resource-boundary-check", + "quality-check", +] + + +def tail_text(path: Path, max_lines: int) -> str: + lines = path.read_text(encoding="utf-8", errors="replace").splitlines() + if len(lines) <= max_lines: + return "\n".join(lines) + hidden = len(lines) - max_lines + tail = "\n".join(lines[-max_lines:]) + return f"... truncated {hidden} earlier lines ...\n{tail}" + + +def run_target(target: str, index: int, total: int, tail_lines: int) -> None: + print(f"[{index}/{total}] {target}") + start = time.perf_counter() + with tempfile.NamedTemporaryFile(prefix=f"ci-{target}-", suffix=".log", delete=False) as handle: + log_path = Path(handle.name) + child_env = {key: os.environ[key] for key in SAFE_ENV_KEYS if key in os.environ} + child_env["CI"] = "1" + try: + with log_path.open("w", encoding="utf-8") as log_file: + proc = subprocess.run( + ["make", "--silent", target], + cwd=ROOT, + stdout=log_file, + stderr=subprocess.STDOUT, + text=True, + env=child_env, + ) + elapsed = time.perf_counter() - start + if proc.returncode != 0: + print(f"FAILED {target} ({elapsed:.1f}s)") + print(f"--- {target} log ---") + print(tail_text(log_path, tail_lines)) + raise SystemExit(proc.returncode) + print(f"OK {target} ({elapsed:.1f}s)") + finally: + if log_path.exists(): + log_path.unlink() + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run CI test targets with compact logging.") + parser.add_argument("--tail-lines", type=int, default=200, help="How many log lines to show on failure.") + parser.add_argument("targets", nargs="*", default=DEFAULT_TARGETS, help="Optional target override.") + args = parser.parse_args() + + total = len(args.targets) + for index, target in enumerate(args.targets, start=1): + run_target(target, index, total, args.tail_lines) + print(f"Completed {total} CI targets.") + + +if __name__ == "__main__": + main() diff --git a/scripts/collect_feedback.py b/scripts/collect_feedback.py new file mode 100644 index 0000000..d2d973f --- /dev/null +++ b/scripts/collect_feedback.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import datetime +from pathlib import Path + + +def load_entries(path: Path) -> list[dict]: + if not path.exists(): + return [] + payload = json.loads(path.read_text(encoding="utf-8")) + entries = payload.get("entries", []) if isinstance(payload, dict) else [] + return entries if isinstance(entries, list) else [] + + +def summarize(entries: list[dict]) -> dict: + if not entries: + return { + "count": 0, + "average_rating": 0, + "latest_category": "none", + "latest_note": "", + } + ratings = [entry["rating"] for entry in entries if isinstance(entry.get("rating"), int)] + latest = entries[-1] + return { + "count": len(entries), + "average_rating": round(sum(ratings) / len(ratings), 2) if ratings else 0, + "latest_category": latest.get("category", "general"), + "latest_note": latest.get("note", ""), + } + + +def render_markdown(payload: dict) -> str: + lines = [ + "# Feedback Log", + "", + f"- Entries: `{payload['summary']['count']}`", + f"- Average rating: `{payload['summary']['average_rating']}`", + "", + "## Recent Feedback", + "", + ] + if not payload["entries"]: + lines.append("- No feedback captured yet.") + for entry in reversed(payload["entries"][-10:]): + lines.extend( + [ + f"### {entry['created_at']}", + f"- Category: `{entry['category']}`", + f"- Rating: `{entry['rating']}`", + f"- Note: {entry['note']}", + f"- Recommended action: {entry['recommended_action']}", + "", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def collect_feedback( + skill_dir: Path, + note: str | None = None, + rating: int = 3, + category: str = "general", + recommended_action: str = "review", + output_json: Path | None = None, + output_md: Path | None = None, +) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_json = output_json or reports_dir / "feedback-log.json" + output_md = output_md or reports_dir / "feedback-log.md" + + entries = load_entries(output_json) + if note: + entries.append( + { + "created_at": datetime.now().isoformat(timespec="seconds"), + "category": category, + "rating": max(1, min(rating, 5)), + "note": note, + "recommended_action": recommended_action, + } + ) + + payload = { + "skill_dir": str(skill_dir), + "entries": entries, + "summary": summarize(entries), + } + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "json": str(output_json), + "markdown": str(output_md), + }, + "summary": payload["summary"], + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Collect lightweight feedback for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--note") + parser.add_argument("--rating", type=int, default=3) + parser.add_argument("--category", default="general") + parser.add_argument("--recommended-action", default="review") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + args = parser.parse_args() + result = collect_feedback( + Path(args.skill_dir), + note=args.note, + rating=args.rating, + category=args.category, + recommended_action=args.recommended_action, + output_json=Path(args.output_json).resolve() if args.output_json else None, + output_md=Path(args.output_md).resolve() if args.output_md else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/compile_skill.py b/scripts/compile_skill.py new file mode 100644 index 0000000..997e722 --- /dev/null +++ b/scripts/compile_skill.py @@ -0,0 +1,447 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + +from skill_ir_paths import find_skill_ir as find_skill_ir_document +from compile_skill_targets import TARGET_TRANSFORMS, target_native_contract, target_permission_contract + + +ROOT = Path(__file__).resolve().parent.parent +COMPILER_SCHEMA_VERSION = "1.0" +COMPILER_NAME = "yao-skill-ir-compiler" + + +def display_path(path: Path, root: Path = ROOT) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +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 read_frontmatter(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + text = path.read_text(encoding="utf-8", errors="replace") + if not text.startswith("---"): + return {} + parts = text.split("---", 2) + if len(parts) < 3: + return {} + if yaml is not None: + payload = yaml.safe_load(parts[1]) or {} + return payload if isinstance(payload, dict) else {} + data: dict[str, Any] = {} + for line in parts[1].splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data + + +def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict[str, Any], str]: + return find_skill_ir_document(skill_dir, name, fallback_source="frontmatter-fallback") + + +def count_list(payload: dict[str, Any], key: str) -> int: + value = payload.get(key, []) + return len(value) if isinstance(value, list) else 0 + + +def list_or_empty(payload: dict[str, Any], key: str) -> list[str]: + value = payload.get(key, []) + return [str(item) for item in value] if isinstance(value, list) else [] + + +def resource_counts(resources: dict[str, Any]) -> dict[str, int]: + return {key: count_list(resources, key) for key in ("references", "scripts", "assets", "reports")} + + +def eval_counts(eval_plan: dict[str, Any]) -> dict[str, int]: + return { + "trigger": count_list(eval_plan, "trigger"), + "output": count_list(eval_plan, "output"), + "adversarial": count_list(eval_plan, "adversarial"), + "baseline": 1 if eval_plan.get("baseline") else 0, + } + + +def scripts_with_flag(scripts: list[dict[str, Any]], flag: str) -> list[str]: + return [str(item.get("path")) for item in scripts if item.get(flag)] + + +def capability(name: str, scripts: list[str], review_reason: str) -> dict[str, Any]: + return { + "required": bool(scripts), + "script_count": len(scripts), + "scripts": scripts, + "review_reason": review_reason if scripts else "", + } + + +def permission_contract(skill_dir: Path) -> dict[str, Any]: + trust_path = skill_dir / "reports" / "security_trust_report.json" + trust = load_json(trust_path) + scripts = trust.get("scripts", []) if isinstance(trust.get("scripts"), list) else [] + network_scripts = scripts_with_flag(scripts, "uses_network") + file_write_scripts = scripts_with_flag(scripts, "uses_file_write") + subprocess_scripts = scripts_with_flag(scripts, "uses_subprocess") + interactive_scripts = scripts_with_flag(scripts, "uses_input") + capabilities = { + "network": capability("network", network_scripts, "Outbound hosts must match security/network_policy.json."), + "file_write": capability("file_write", file_write_scripts, "Scripts write local files or generated artifacts."), + "subprocess": capability("subprocess", subprocess_scripts, "Scripts spawn local commands and need operator review."), + "interactive": capability("interactive", interactive_scripts, "Scripts prompt for user input or credentials."), + } + declared = [name for name, item in capabilities.items() if item["required"]] + help_smoke = trust.get("help_smoke", {}) if isinstance(trust.get("help_smoke"), dict) else {} + network_policy = trust.get("network_policy", {}) if isinstance(trust.get("network_policy"), dict) else {} + summary = trust.get("summary", {}) if isinstance(trust.get("summary"), dict) else {} + return { + "schema_version": "1.0", + "source": display_path(trust_path, skill_dir) if trust else "missing-security-trust-report", + "source_available": bool(trust), + "declared_capabilities": declared, + "review_required": bool(declared), + "capabilities": capabilities, + "network_policy": { + "source": network_policy.get("path", "security/network_policy.json"), + "covered_scripts": network_policy.get("covered_scripts", []), + "missing_scripts": network_policy.get("missing_scripts", []), + "mismatches": network_policy.get("mismatches", []), + }, + "help_smoke": { + "enabled": bool(help_smoke.get("enabled")), + "checked_count": int(help_smoke.get("checked_count", 0) or 0), + "failed_count": int(help_smoke.get("failed_count", 0) or 0), + "failed_scripts": help_smoke.get("failed_scripts", []), + }, + "trust_summary": { + "secret_findings": int(summary.get("secret_findings", 0) or 0), + "network_script_count": int(summary.get("network_script_count", len(network_scripts)) or 0), + "file_write_script_count": int(summary.get("file_write_script_count", len(file_write_scripts)) or 0), + "subprocess_script_count": len(subprocess_scripts), + "interactive_script_count": int(summary.get("interactive_script_count", len(interactive_scripts)) or 0), + "help_smoke_failed_count": int(summary.get("help_smoke_failed_count", 0) or 0), + }, + } + + +def load_sources(skill_dir: Path) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + frontmatter = read_frontmatter(skill_dir / "SKILL.md") + manifest = load_json(skill_dir / "manifest.json") + interface_doc = load_yaml(skill_dir / "agents" / "interface.yaml") + name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name) + ir, ir_source = find_skill_ir(skill_dir, name) + return { + "skill_dir": skill_dir, + "frontmatter": frontmatter, + "manifest": manifest, + "interface_doc": interface_doc, + "interface": interface_doc.get("interface", {}), + "compatibility": interface_doc.get("compatibility", {}), + "ir": ir, + "ir_source": ir_source, + } + + +def declared_targets(sources: dict[str, Any]) -> list[str]: + ir = sources["ir"] + compatibility = sources["compatibility"] + manifest = sources["manifest"] + candidates = ( + ir.get("targets") + if isinstance(ir.get("targets"), list) + else manifest.get("target_platforms") + or compatibility.get("adapter_targets") + or [] + ) + targets = [] + for item in candidates: + value = str(item).strip() + if value and value not in targets: + targets.append(value) + return targets or ["generic"] + + +def semantic_source(sources: dict[str, Any]) -> dict[str, Any]: + skill_dir = sources["skill_dir"] + frontmatter = sources["frontmatter"] + manifest = sources["manifest"] + interface = sources["interface"] + compatibility = sources["compatibility"] + ir = sources["ir"] + trigger = ir.get("trigger_surface", {}) if isinstance(ir.get("trigger_surface"), dict) else {} + workflow = ir.get("workflow", {}) if isinstance(ir.get("workflow"), dict) else {} + resources = ir.get("resources", {}) if isinstance(ir.get("resources"), dict) else {} + eval_plan = ir.get("eval_plan", {}) if isinstance(ir.get("eval_plan"), dict) else {} + description = str(trigger.get("description") or frontmatter.get("description") or "") + name = str(ir.get("name") or frontmatter.get("name") or manifest.get("name") or skill_dir.name) + permissions = permission_contract(skill_dir) + return { + "name": name, + "title": str(ir.get("title") or interface.get("display_name") or name), + "version": str(manifest.get("version") or frontmatter.get("version") or "1.0.0"), + "description": description, + "job_to_be_done": str(ir.get("job_to_be_done") or description), + "trigger": { + "description": description, + "should_trigger": list_or_empty(trigger, "should_trigger"), + "should_not_trigger": list_or_empty(trigger, "should_not_trigger"), + "edge_cases": list_or_empty(trigger, "edge_cases"), + }, + "workflow": { + "steps": list_or_empty(workflow, "steps"), + "decision_points": list_or_empty(workflow, "decision_points"), + "failure_modes": list_or_empty(workflow, "failure_modes"), + }, + "resources": { + "references": list_or_empty(resources, "references"), + "scripts": list_or_empty(resources, "scripts"), + "assets": list_or_empty(resources, "assets"), + "reports": list_or_empty(resources, "reports"), + "counts": resource_counts(resources), + }, + "eval_plan": { + "trigger": list_or_empty(eval_plan, "trigger"), + "output": list_or_empty(eval_plan, "output"), + "adversarial": list_or_empty(eval_plan, "adversarial"), + "baseline": str(eval_plan.get("baseline") or ""), + "counts": eval_counts(eval_plan), + }, + "risk": ir.get("risk", {}) if isinstance(ir.get("risk"), dict) else {}, + "governance": ir.get("governance", {}) if isinstance(ir.get("governance"), dict) else {}, + "runtime": { + "activation": compatibility.get("activation", {}) if isinstance(compatibility.get("activation"), dict) else {}, + "execution": compatibility.get("execution", {}) if isinstance(compatibility.get("execution"), dict) else {}, + "trust": compatibility.get("trust", {}) if isinstance(compatibility.get("trust"), dict) else {}, + "adapter_targets": compatibility.get("adapter_targets", []), + "canonical_format": compatibility.get("canonical_format", "agent-skills"), + }, + "permissions": permissions, + "source_files_count": count_list(ir, "source_files") if ir else 0, + } + + +def compile_target_contract(skill_dir: Path, target: str) -> dict[str, Any]: + sources = load_sources(skill_dir) + compatibility = sources["compatibility"] + degradation = compatibility.get("degradation", {}) if isinstance(compatibility.get("degradation"), dict) else {} + declared = declared_targets(sources) + failures: list[str] = [] + warnings: list[str] = [] + profile = TARGET_TRANSFORMS.get(target) + if profile is None: + failures.append(f"Unsupported compiler target: {target}") + profile = { + "adapter_mode": "unsupported", + "generated_files": [], + "metadata_mapping": {}, + "preserved_semantics": [], + "unsupported_features": [], + } + if target not in declared and not (target in {"agent-skills", "vscode"} and "agent-skills-compatible" in declared): + warnings.append(f"Target is not declared in Skill IR or interface metadata: {target}") + if not sources["ir"]: + warnings.append("Skill IR is missing; compiler used frontmatter fallback") + + contract = semantic_source(sources) + permissions = contract["permissions"] + target_permissions = target_permission_contract(target, permissions) + target_native = target_native_contract(target, profile, contract, target_permissions) + contract["target"] = target + contract["target_permission_contract"] = target_permissions + contract["target_native_contract"] = target_native + contract["degradation_strategy"] = degradation.get(target, degradation.get("generic", "neutral-source")) + contract["target_runtime"] = { + "adapter_mode": profile["adapter_mode"], + "generated_files": profile["generated_files"], + "metadata_mapping": profile["metadata_mapping"], + "preserved_semantics": profile["preserved_semantics"], + "native_surface": target_native["native_surface"], + } + + return { + "schema_version": COMPILER_SCHEMA_VERSION, + "target": target, + "status": "block" if failures else ("warn" if warnings else "pass"), + "compiler": { + "name": COMPILER_NAME, + "schema_version": COMPILER_SCHEMA_VERSION, + "source": "skill-ir" if sources["ir"] else "frontmatter-fallback", + "ir_source": sources["ir_source"], + "ir_schema_version": str(sources["ir"].get("schema_version") or "none"), + }, + "source": { + "skill_dir": display_path(sources["skill_dir"]), + "canonical_metadata": "agents/interface.yaml" if (sources["skill_dir"] / "agents" / "interface.yaml").exists() else "missing", + "declared_targets": declared, + }, + "compiled_contract": contract, + "permission_contract": permissions, + "target_permission_contract": target_permissions, + "target_native_contract": target_native, + "target_transform": { + "target": target, + "adapter_mode": profile["adapter_mode"], + "generated_files": profile["generated_files"], + "metadata_mapping": profile["metadata_mapping"], + "preserved_semantics": profile["preserved_semantics"], + "degradation_strategy": contract["degradation_strategy"], + "permission_representation": target_permissions["representation"], + "native_surface": target_native["native_surface"], + "activation_policy": target_native["activation"]["policy"], + "resource_strategy": target_native["resources"]["strategy"], + "script_strategy": target_native["scripts"]["strategy"], + "permission_enforcement": target_native["permissions"]["enforcement"], + }, + "unsupported_features": list(profile.get("unsupported_features", [])), + "warnings": warnings, + "failures": failures, + } + + +def compile_targets(skill_dir: Path, targets: list[str] | None = None, generated_at: str | None = None) -> dict[str, Any]: + sources = load_sources(skill_dir) + selected = targets or declared_targets(sources) + compiled = [compile_target_contract(skill_dir, target) for target in selected] + failures = [failure for item in compiled for failure in item["failures"]] + warnings = [warning for item in compiled for warning in item["warnings"]] + return { + "schema_version": COMPILER_SCHEMA_VERSION, + "ok": not failures, + "generated_at": generated_at or str(date.today()), + "skill_dir": display_path(Path(skill_dir).resolve()), + "summary": { + "target_count": len(compiled), + "pass_count": sum(1 for item in compiled if item["status"] == "pass"), + "warn_count": sum(1 for item in compiled if item["status"] == "warn"), + "block_count": sum(1 for item in compiled if item["status"] == "block"), + "failure_count": len(failures), + "warning_count": len(warnings), + }, + "targets": compiled, + "failures": failures, + "warnings": warnings, + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Compiled Targets", + "", + f"- OK: `{report['ok']}`", + f"- Targets: `{summary['target_count']}`", + f"- Pass: `{summary['pass_count']}`", + f"- Warn: `{summary['warn_count']}`", + f"- Block: `{summary['block_count']}`", + "", + "## Target Transforms", + "", + "| Target | Status | Native Surface | Adapter Mode | Permissions | Degradation | Generated Files |", + "| --- | --- | --- | --- | --- | --- | --- |", + ] + for item in report["targets"]: + transform = item["target_transform"] + files = ", ".join(transform.get("generated_files", [])) or "none" + permissions = ", ".join(item.get("target_permission_contract", {}).get("declared_capabilities", [])) or "none" + lines.append( + f"| `{item['target']}` | `{item['status']}` | {transform.get('native_surface', '')} | `{transform.get('adapter_mode', '')}` | `{permissions}` | `{transform.get('degradation_strategy', '')}` | {files} |" + ) + lines.extend(["", "## Native Behavior Contracts", ""]) + for item in report["targets"]: + native = item.get("target_native_contract", {}) + permissions = native.get("permissions", {}) + review = native.get("review", {}) + lines.extend( + [ + f"### {item['target']}", + "", + f"- Native surface: {native.get('native_surface', '')}", + f"- Activation: {native.get('activation', {}).get('policy', '')}", + f"- Resources: {native.get('resources', {}).get('strategy', '')}", + f"- Scripts: {native.get('scripts', {}).get('strategy', '')}", + f"- Permission enforcement: `{permissions.get('enforcement', '')}`; native enforcement `{permissions.get('native_enforcement')}`", + f"- Review artifacts: {', '.join(review.get('artifacts', [])) or 'none'}", + "", + ] + ) + lines.extend(["", "## Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) + lines.extend(["", "## Warnings", ""]) + lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"]) + return "\n".join(lines).strip() + "\n" + + +def render_compile_report( + skill_dir: Path, + targets: list[str] | None = None, + output_json: Path | None = None, + output_md: Path | None = None, + generated_at: str | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + reports = skill_dir / "reports" + reports.mkdir(parents=True, exist_ok=True) + output_json = output_json or reports / "compiled_targets.json" + output_md = output_md or reports / "compiled_targets.md" + report = compile_targets(skill_dir, targets=targets, generated_at=generated_at) + report["artifacts"] = { + "json": display_path(output_json), + "markdown": display_path(output_md), + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Compile Skill IR into target-specific semantic contracts.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--target", action="append", default=[]) + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--generated-at") + args = parser.parse_args() + + payload = render_compile_report( + Path(args.skill_dir), + targets=args.target or None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + output_md=Path(args.output_md).resolve() if args.output_md else None, + generated_at=args.generated_at, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/compile_skill_targets.py b/scripts/compile_skill_targets.py new file mode 100644 index 0000000..414740f --- /dev/null +++ b/scripts/compile_skill_targets.py @@ -0,0 +1,282 @@ +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by compile_skill for target platform model and contract builders." + + +TARGET_TRANSFORMS: dict[str, dict[str, Any]] = { + "openai": { + "adapter_mode": "metadata-adapter", + "generated_files": ["targets/openai/adapter.json", "targets/openai/agents/openai.yaml"], + "metadata_mapping": { + "display_name": "targets/openai/agents/openai.yaml::interface.display_name", + "default_prompt": "targets/openai/agents/openai.yaml::interface.default_prompt", + "activation": "targets/openai/agents/openai.yaml::compatibility.activation_mode", + "execution": "targets/openai/agents/openai.yaml::compatibility.execution_context", + "trust": "targets/openai/agents/openai.yaml::compatibility.trust_level", + "permissions": "targets/openai/agents/openai.yaml::compatibility.permission_contract", + "degradation": "targets/openai/agents/openai.yaml::compatibility.degradation_strategy", + }, + "preserved_semantics": ["trigger", "workflow-counts", "resources", "eval-plan", "risk", "governance", "runtime", "trust", "permissions"], + "unsupported_features": ["client-native script permission prompts are represented as permission contract metadata"], + }, + "claude": { + "adapter_mode": "neutral-source-plus-adapter", + "generated_files": ["targets/claude/adapter.json", "targets/claude/README.md"], + "metadata_mapping": { + "display_name": "targets/claude/adapter.json::display_name", + "default_prompt": "targets/claude/adapter.json::default_prompt", + "activation": "targets/claude/adapter.json::activation_mode", + "execution": "targets/claude/adapter.json::execution_context", + "trust": "targets/claude/adapter.json::trust_level", + "permissions": "targets/claude/adapter.json::target_permission_contract", + "degradation": "targets/claude/adapter.json::degradation_strategy", + }, + "preserved_semantics": ["trigger", "workflow-counts", "resources", "eval-plan", "risk", "governance", "runtime", "trust", "permissions"], + "unsupported_features": ["vendor-native metadata fields are carried as adapter JSON and README notes"], + }, + "generic": { + "adapter_mode": "agent-skills-compatible", + "generated_files": ["targets/generic/adapter.json"], + "metadata_mapping": { + "display_name": "targets/generic/adapter.json::display_name", + "default_prompt": "targets/generic/adapter.json::default_prompt", + "activation": "targets/generic/adapter.json::activation_mode", + "execution": "targets/generic/adapter.json::execution_context", + "trust": "targets/generic/adapter.json::trust_level", + "permissions": "targets/generic/adapter.json::target_permission_contract", + "degradation": "targets/generic/adapter.json::degradation_strategy", + }, + "preserved_semantics": ["trigger", "workflow-counts", "resources", "eval-plan", "risk", "governance", "runtime", "trust", "permissions"], + "unsupported_features": [], + }, + "agent-skills-compatible": { + "adapter_mode": "neutral-agent-skills-source", + "generated_files": ["SKILL.md", "agents/interface.yaml"], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json", + }, + "preserved_semantics": ["trigger", "workflow", "resources", "eval-plan", "risk", "governance", "runtime", "trust", "permissions"], + "unsupported_features": [], + }, + "agent-skills": { + "adapter_mode": "neutral-agent-skills-source", + "generated_files": ["SKILL.md", "agents/interface.yaml"], + "metadata_mapping": { + "name": "SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "manifest": "manifest.json", + }, + "preserved_semantics": ["trigger", "workflow", "resources", "eval-plan", "risk", "governance", "runtime", "trust", "permissions"], + "unsupported_features": [], + }, + "vscode": { + "adapter_mode": "vscode-agent-skills-adapter", + "generated_files": ["targets/vscode/adapter.json", "targets/vscode/README.md"], + "metadata_mapping": { + "name": "folder-name-and-SKILL.md::frontmatter.name", + "description": "SKILL.md::frontmatter.description", + "interface": "agents/interface.yaml", + "permissions": "targets/vscode/adapter.json::target_permission_contract", + "install_scope": "targets/vscode/README.md", + }, + "preserved_semantics": ["trigger", "workflow", "resources", "eval-plan", "risk", "governance", "runtime", "trust", "permissions"], + "unsupported_features": ["VS Code installation scope is documented but not installed by this compiler"], + }, +} + + +TARGET_PERMISSION_MODELS = { + "openai": { + "model": "metadata-only", + "native_enforcement": False, + "representation": "targets/openai/agents/openai.yaml::compatibility.permission_contract plus adapter.json", + "operator_note": "OpenAI target carries permission metadata for reviewer visibility; host enforcement remains outside the package.", + }, + "claude": { + "model": "neutral-source-plus-adapter", + "native_enforcement": False, + "representation": "targets/claude/adapter.json::target_permission_contract and README notes", + "operator_note": "Claude-compatible package keeps permission intent in adapter metadata for install review.", + }, + "generic": { + "model": "agent-skills-compatible-metadata", + "native_enforcement": False, + "representation": "targets/generic/adapter.json::target_permission_contract", + "operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review.", + }, + "vscode": { + "model": "vscode-workspace-trust-plus-metadata", + "native_enforcement": False, + "representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes", + "operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks.", + }, +} + + +TARGET_NATIVE_MODELS = { + "openai": { + "native_surface": "OpenAI-style interface metadata plus neutral Agent Skills source", + "activation_policy": "Use frontmatter description for catalog routing and targets/openai/agents/openai.yaml for display name, default prompt, and compatibility metadata.", + "resource_strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", + "script_strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", + "permission_enforcement": "metadata-only", + "install_scope": "plugin or skill package consumer", + "review_artifacts": ["targets/openai/agents/openai.yaml", "targets/openai/adapter.json", "reports/review-studio.html"], + "fallback_behavior": "If OpenAI-native metadata is ignored, the package remains readable as neutral Agent Skills source.", + "unsupported_native_features": [ + "client-native permission prompts", + "provider-executed scripts", + ], + }, + "claude": { + "native_surface": "Claude-compatible neutral source folder with adapter notes", + "activation_policy": "Use SKILL.md frontmatter description as the primary activation contract and adapter.json for review metadata.", + "resource_strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", + "script_strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", + "permission_enforcement": "metadata-fallback", + "install_scope": "user or project skill directory", + "review_artifacts": ["targets/claude/README.md", "targets/claude/adapter.json", "reports/review-studio.html"], + "fallback_behavior": "If Claude-specific metadata is not consumed, SKILL.md and references remain the source of truth.", + "unsupported_native_features": [ + "vendor-native permission enforcement", + "provider-specific execution transforms", + ], + }, + "generic": { + "native_surface": "Agent Skills compatible neutral package", + "activation_policy": "Use SKILL.md name and description; consumers decide automatic or manual activation.", + "resource_strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", + "script_strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "install_scope": "generic Agent Skills compatible root", + "review_artifacts": ["targets/generic/adapter.json", "reports/review-studio.html"], + "fallback_behavior": "Neutral source is the runtime fallback.", + "unsupported_native_features": [], + }, + "agent-skills-compatible": { + "native_surface": "Agent Skills standard source tree", + "activation_policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "resource_strategy": "Keep optional directories as relative resources next to SKILL.md.", + "script_strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "install_scope": "Agent Skills source root", + "review_artifacts": ["SKILL.md", "agents/interface.yaml", "reports/review-studio.html"], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [], + }, + "agent-skills": { + "native_surface": "Agent Skills standard source tree", + "activation_policy": "Use SKILL.md frontmatter name and description for progressive disclosure.", + "resource_strategy": "Keep optional directories as relative resources next to SKILL.md.", + "script_strategy": "Scripts remain local optional resources and should advertise --help when executable.", + "permission_enforcement": "consumer-enforced-or-metadata-only", + "install_scope": "Agent Skills source root", + "review_artifacts": ["SKILL.md", "agents/interface.yaml", "reports/review-studio.html"], + "fallback_behavior": "The source tree itself is the target artifact.", + "unsupported_native_features": [], + }, + "vscode": { + "native_surface": "VS Code/Copilot Agent Skills project or user scope", + "activation_policy": "Use folder name plus SKILL.md name/description; keep description under platform limits.", + "resource_strategy": "Install as project or user scoped skill source, preserving relative references and scripts.", + "script_strategy": "Scripts require workspace trust and operator/client approval outside this compiler.", + "permission_enforcement": "client-or-workspace-trust", + "install_scope": "VS Code user or project skills directory", + "review_artifacts": ["SKILL.md", "agents/interface.yaml", "reports/review-studio.html"], + "fallback_behavior": "If VS Code scope is not installed, use the neutral Agent Skills source.", + "unsupported_native_features": [ + "automatic VS Code installation", + ], + }, +} + + +def target_permission_contract(target: str, permissions: dict[str, Any]) -> dict[str, Any]: + model = TARGET_PERMISSION_MODELS.get( + target, + { + "model": "metadata-only", + "native_enforcement": False, + "representation": "adapter metadata", + "operator_note": "Permission semantics are preserved as metadata for reviewer visibility.", + }, + ) + return { + "schema_version": "1.0", + "target": target, + "permission_model": model["model"], + "native_enforcement": model["native_enforcement"], + "representation": model["representation"], + "review_required": bool(permissions.get("review_required")), + "declared_capabilities": permissions.get("declared_capabilities", []), + "capability_counts": { + name: item.get("script_count", 0) + for name, item in permissions.get("capabilities", {}).items() + }, + "evidence": permissions.get("source", ""), + "operator_note": model["operator_note"], + } + + +def target_native_contract( + target: str, + profile: dict[str, Any], + contract: dict[str, Any], + target_permissions: dict[str, Any], +) -> dict[str, Any]: + model = TARGET_NATIVE_MODELS.get( + target, + { + "native_surface": "adapter metadata", + "activation_policy": "Carry activation semantics as metadata for the target consumer.", + "resource_strategy": "Preserve neutral package resources.", + "script_strategy": "Expose script metadata for reviewer visibility.", + "permission_enforcement": "metadata-only", + "install_scope": "target consumer", + "review_artifacts": ["adapter.json", "reports/review-studio.html"], + "fallback_behavior": "Use neutral source package.", + "unsupported_native_features": [], + }, + ) + return { + "schema_version": "1.0", + "target": target, + "native_surface": model["native_surface"], + "activation": { + "policy": model["activation_policy"], + "trigger_description": contract.get("trigger", {}).get("description", ""), + "manual_activation_supported": True, + "automatic_activation_note": "Depends on the target client route/catalog implementation.", + }, + "resources": { + "strategy": model["resource_strategy"], + "counts": contract.get("resources", {}).get("counts", {}), + "generated_files": profile.get("generated_files", []), + }, + "scripts": { + "strategy": model["script_strategy"], + "script_count": contract.get("resources", {}).get("counts", {}).get("scripts", 0), + "help_smoke_failed_count": contract.get("permissions", {}).get("help_smoke", {}).get("failed_count", 0), + }, + "permissions": { + "enforcement": model["permission_enforcement"], + "native_enforcement": bool(target_permissions.get("native_enforcement")), + "declared_capabilities": target_permissions.get("declared_capabilities", []), + "review_required": bool(target_permissions.get("review_required")), + }, + "review": { + "artifacts": model["review_artifacts"], + "fallback_behavior": model["fallback_behavior"], + "unsupported_native_features": [ + *model.get("unsupported_native_features", []), + *profile.get("unsupported_features", []), + ], + }, + "install_scope": model["install_scope"], + } diff --git a/scripts/context_sizer.py b/scripts/context_sizer.py new file mode 100644 index 0000000..5abc563 --- /dev/null +++ b/scripts/context_sizer.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +TEXT_EXTS = {".css", ".md", ".txt", ".yaml", ".yml", ".json", ".py", ".sh", ".js", ".ts"} +IGNORED_RELATIVE_DIRS = { + Path("reports") / "release_snapshots", + Path("tests") / "tmp", + Path("tests") / "tmp_snapshot", + Path("tests") / "tmp_cli", +} +PACKAGE_PATHS = ( + "SKILL.md", + "manifest.json", + "agents", + "references", + "scripts", + "assets", + "evals", + "templates", + "reports", + "failures", + "tests", + "input", + "outputs", +) +IGNORED_FILE_PATTERNS = { + "reports/benchmark_reproducibility*.json", + "reports/benchmark_reproducibility*.md", + "reports/context_budget*.json", + "reports/context_budget*.md", + "reports/evidence_consistency*.json", + "reports/evidence_consistency*.md", + "reports/review-studio*.html", + "reports/review-studio*.json", + "reports/review-viewer*.html", + "reports/review-viewer*.json", + "reports/skill-interpretation*.html", + "reports/skill-interpretation*.json", + "reports/skill-overview*.html", + "reports/skill-overview*.json", + "reports/world_class_evidence_preflight*.json", + "reports/world_class_evidence_preflight*.md", + "reports/world_class_evidence_preflight*.html", + "reports/*pattern-analysis*.md", + "reports/*research-plan*.md", +} + + +def estimate_tokens(text: str) -> int: + # Fast heuristic suitable for local gating. + return max(1, len(text) // 4) + + +def read_text(path: Path) -> str: + try: + return path.read_text(encoding="utf-8") + except UnicodeDecodeError: + return path.read_text(encoding="utf-8", errors="ignore") + + +def classify(path: Path) -> str: + parts = set(path.parts) + if path == Path("SKILL.md"): + return "skill_body" + if "agents" in parts: + return "interface" + if "references" in parts: + return "reference" + if "scripts" in parts: + return "script" + if "assets" in parts: + return "asset" + if path.suffix in TEXT_EXTS: + return "other_text" + return "binary_or_other" + + +def should_ignore(path: Path, skill_dir: Path) -> bool: + rel = path.relative_to(skill_dir) + if any(rel == ignored or ignored in rel.parents for ignored in IGNORED_RELATIVE_DIRS): + return True + if any(rel.match(pattern) for pattern in IGNORED_FILE_PATTERNS): + return True + return len(rel.parts) >= 2 and rel.parts[0] == "tests" and rel.parts[1].startswith("tmp_") + + +def summarize(skill_dir: Path) -> dict: + files = [] + total_tokens = 0 + initial_tokens = 0 + candidate_files = [] + for entry in PACKAGE_PATHS: + path = skill_dir / entry + if path.is_file(): + candidate_files.append(path) + elif path.is_dir(): + candidate_files.extend(sorted(file for file in path.rglob("*") if file.is_file())) + + for path in candidate_files: + if should_ignore(path, skill_dir): + continue + rel = path.relative_to(skill_dir) + if path.suffix not in TEXT_EXTS and path.name != "SKILL.md": + size = path.stat().st_size + files.append({"path": str(rel), "kind": "binary_or_other", "bytes": size}) + continue + kind = classify(rel) + if kind in {"binary_or_other", "asset"} and path.suffix not in TEXT_EXTS: + size = path.stat().st_size + files.append({"path": str(rel), "kind": kind, "bytes": size}) + continue + text = read_text(path) + tokens = estimate_tokens(text) + record = { + "path": str(rel), + "kind": kind, + "chars": len(text), + "estimated_tokens": tokens, + } + files.append(record) + total_tokens += tokens + if rel == Path("SKILL.md") or (rel.parts and rel.parts[0] == "agents"): + initial_tokens += tokens + return { + "skill_dir": str(skill_dir), + "estimated_initial_load_tokens": initial_tokens, + "estimated_total_text_tokens": total_tokens, + "warning": initial_tokens > 2000, + "files": files, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Estimate context size for a skill package.") + parser.add_argument("skill_dir", help="Path to the skill directory") + parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON") + args = parser.parse_args() + + report = summarize(Path(args.skill_dir).resolve()) + if args.json: + print(json.dumps(report, ensure_ascii=False, indent=2)) + return + + print(f"Skill: {report['skill_dir']}") + print(f"Estimated initial-load tokens: {report['estimated_initial_load_tokens']}") + print(f"Estimated total text tokens: {report['estimated_total_text_tokens']}") + print(f"Initial-load warning (>2000): {'YES' if report['warning'] else 'NO'}") + print("") + for file in report["files"]: + if "estimated_tokens" in file: + print(f"{file['kind']:12} {file['estimated_tokens']:6}t {file['path']}") + else: + print(f"{file['kind']:12} {file['bytes']:6}b {file['path']}") + + +if __name__ == "__main__": + main() diff --git a/scripts/create_iteration_snapshot.py b/scripts/create_iteration_snapshot.py new file mode 100644 index 0000000..d86d17c --- /dev/null +++ b/scripts/create_iteration_snapshot.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from datetime import date +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + +OPTIMIZATION_REPORTS = { + "yao-meta-skill": ROOT / "reports" / "description_optimization.json", + "team-frontend-review": ROOT / "examples" / "team-frontend-review" / "optimization" / "reports" / "description_optimization.json", + "governed-incident-command": ROOT + / "examples" + / "governed-incident-command" + / "optimization" + / "reports" + / "description_optimization.json", +} + +CONTEXT_REPORTS = { + "yao-meta-skill": ROOT / "reports" / "context_budget.json", + "team-frontend-review": ROOT + / "examples" + / "team-frontend-review" + / "generated-skill" + / "reports" + / "context_budget.json", + "governed-incident-command": ROOT + / "examples" + / "governed-incident-command" + / "generated-skill" + / "reports" + / "context_budget.json", +} + + +def load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def relative(path: Path) -> str: + return str(path.relative_to(ROOT)) + + +def slugify(text: str) -> str: + value = re.sub(r"[^a-z0-9]+", "-", text.strip().lower()) + return value.strip("-") or "snapshot" + + +def summarize_target(target: dict, route_scorecard: dict, optimization_report: dict, context_report: dict) -> dict: + route_stats = route_scorecard.get("route_stats", {}).get(target["name"], {}) + winner_summary = optimization_report.get("winner", {}) + holdout = winner_summary.get("holdout", {}) + blind = winner_summary.get("blind_holdout", {}) + judge_blind = ((target.get("judge_blind") or {}).get("winner") or {}) + adversarial = winner_summary.get("adversarial_holdout", {}) + adversarial_calibration = ((target.get("calibration") or {}).get("adversarial_holdout") or {}) + return { + "winner_label": target["winner_label"], + "decision": target["decision"], + "visible_holdout_total_errors": holdout.get("false_positives", 0) + holdout.get("false_negatives", 0), + "blind_holdout_total_errors": blind.get("false_positives", 0) + blind.get("false_negatives", 0), + "judge_blind_total_errors": target.get("winner_judge_blind_holdout_total_errors"), + "judge_blind_agreement": judge_blind.get("agreement_rate"), + "adversarial_total_errors": adversarial.get("false_positives", 0) + adversarial.get("false_negatives", 0), + "adversarial_gap": adversarial_calibration.get("score_gap"), + "adversarial_risk_band": adversarial_calibration.get("risk_band"), + "route_precision": route_stats.get("precision"), + "route_recall": route_stats.get("recall"), + "route_average_margin": route_stats.get("average_margin"), + "context_initial_load_tokens": context_report.get("initial_load_tokens"), + "context_budget": context_report.get("budget"), + "quality_density": context_report.get("quality_density"), + } + + +def render_markdown(snapshot: dict) -> str: + summary = snapshot["summary"] + lines = [ + f"# Release Snapshot: {snapshot['target']}", + "", + f"- label: `{snapshot['label']}`", + f"- date: `{snapshot['date']}`", + f"- decision: `{snapshot['decision']}`", + f"- winner label: `{summary['winner_label']}`", + f"- next action: {snapshot['next_action']}", + "", + "## Promotion Gates", + "", + "| Gate | Pass |", + "| --- | --- |", + ] + for gate, passed in snapshot["gate_status"].items(): + lines.append(f"| `{gate}` | {passed} |") + lines.extend( + [ + "", + "## Quality Summary", + "", + f"- visible holdout total errors: `{summary['visible_holdout_total_errors']}`", + f"- blind holdout total errors: `{summary['blind_holdout_total_errors']}`", + f"- judge blind total errors: `{summary['judge_blind_total_errors']}`", + f"- judge blind agreement: `{summary['judge_blind_agreement']}`", + f"- adversarial total errors: `{summary['adversarial_total_errors']}`", + f"- adversarial gap: `{summary['adversarial_gap']}`", + f"- adversarial risk band: `{summary['adversarial_risk_band']}`", + f"- route precision: `{summary['route_precision']}`", + f"- route recall: `{summary['route_recall']}`", + f"- route average margin: `{summary['route_average_margin']}`", + f"- context initial load: `{summary['context_initial_load_tokens']}/{summary['context_budget']}`", + f"- quality density: `{summary['quality_density']}`", + "", + "## Cause Tags", + "", + ] + ) + for cause in snapshot["cause_tags"]: + lines.append(f"- `{cause}`") + lines.extend(["", "## Artifact Paths", ""]) + for key, path in snapshot["artifacts"].items(): + lines.append(f"- {key}: `{path}`") + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Create a versioned release snapshot from current promotion outputs.") + parser.add_argument("--target", required=True) + parser.add_argument("--label", default="manual") + parser.add_argument("--promotion-decisions", default="reports/promotion_decisions.json") + parser.add_argument("--route-scorecard", default="reports/route_scorecard.json") + parser.add_argument("--output-dir", default="reports/release_snapshots") + args = parser.parse_args() + + promotion_decisions = load_json(ROOT / args.promotion_decisions) + route_scorecard = load_json(ROOT / args.route_scorecard) + target = next((item for item in promotion_decisions["targets"] if item["name"] == args.target), None) + if target is None: + raise SystemExit(f"Unknown promotion target: {args.target}") + + optimization_report = load_json(OPTIMIZATION_REPORTS[args.target]) + context_report = load_json(CONTEXT_REPORTS[args.target]) + label = f"{date.today().isoformat()}-{slugify(args.label)}" + output_dir = ROOT / args.output_dir / args.target + output_dir.mkdir(parents=True, exist_ok=True) + snapshot_json = output_dir / f"{label}.json" + snapshot_md = output_dir / f"{label}.md" + + bundle_dir = ROOT / "reports" / "iteration_bundles" / args.target + summary = summarize_target(target, route_scorecard, optimization_report, context_report) + snapshot = { + "target": args.target, + "label": label, + "date": date.today().isoformat(), + "decision": target["decision"], + "next_action": target["promotion"]["next_action"], + "cause_tags": target["promotion"]["causes"], + "gate_status": target["promotion"]["gate_status"], + "summary": summary, + "artifacts": { + "bundle_json": relative(bundle_dir / "bundle.json"), + "bundle_md": relative(bundle_dir / "bundle.md"), + "review_md": relative(bundle_dir / "review.md"), + "optimization_report": relative(OPTIMIZATION_REPORTS[args.target]), + "promotion_decisions": relative(ROOT / args.promotion_decisions), + "route_scorecard": relative(ROOT / args.route_scorecard), + "context_budget": relative(CONTEXT_REPORTS[args.target]), + "iteration_ledger": "reports/iteration_ledger.md", + }, + } + + snapshot_json.write_text(json.dumps(snapshot, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + snapshot_md.write_text(render_markdown(snapshot), encoding="utf-8") + print( + json.dumps( + { + "ok": True, + "target": args.target, + "label": label, + "artifacts": { + "snapshot_json": relative(snapshot_json), + "snapshot_md": relative(snapshot_md), + "review_md": relative(bundle_dir / "review.md"), + "bundle_md": relative(bundle_dir / "bundle.md"), + }, + }, + ensure_ascii=False, + indent=2, + ) + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/cross_packager.py b/scripts/cross_packager.py new file mode 100644 index 0000000..fe32bc5 --- /dev/null +++ b/scripts/cross_packager.py @@ -0,0 +1,495 @@ +#!/usr/bin/env python3 +import argparse +import json +import shutil +import zipfile +from pathlib import Path, PurePosixPath +import yaml + +from compile_skill import compile_target_contract +from cross_packager_contracts import PLATFORM_CONTRACTS +from skill_ir_paths import find_skill_ir as find_skill_ir_document + + +def display_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def read_json(path: Path) -> dict: + if not path.exists(): + return {} + payload = json.loads(path.read_text(encoding="utf-8")) + return payload if isinstance(payload, dict) else {} + + +def read_simple_yaml(path: Path) -> dict: + return yaml.safe_load(path.read_text(encoding="utf-8")) or {} + + +def read_frontmatter(skill_md: Path) -> dict: + if not skill_md.exists(): + raise FileNotFoundError(f"Missing required file: {skill_md}") + text = skill_md.read_text(encoding="utf-8") + if not text.startswith("---"): + return {} + parts = text.split("---", 2) + if len(parts) < 3: + return {} + return yaml.safe_load(parts[1]) or {} + + +def read_interface(skill_dir: Path) -> dict: + path = skill_dir / "agents" / "interface.yaml" + if not path.exists(): + return {} + raw = read_simple_yaml(path) + return raw + + +def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict, str]: + return find_skill_ir_document(skill_dir, name, fallback_source="frontmatter-fallback") + + +def package_name_from_manifest(manifest: dict, skill_dir: Path) -> str: + name = str(manifest.get("name") or "").strip() + if name: + return name + frontmatter = read_frontmatter(skill_dir / "SKILL.md") + return str(frontmatter.get("name") or skill_dir.name) + + +def require_fields(payload: dict, fields: list[str], label: str) -> None: + missing = [field for field in fields if not payload.get(field)] + if missing: + raise ValueError(f"Missing required {label} fields: {', '.join(missing)}") + + +def require_target_degradation( + degradation: dict, + targets: list[str], +) -> None: + missing = [target for target in targets if not degradation.get(target)] + if missing: + raise ValueError(f"Missing degradation entries for targets: {', '.join(missing)}") + + +def count_list(payload: dict, key: str) -> int: + value = payload.get(key, []) + return len(value) if isinstance(value, list) else 0 + + +def resource_counts(resources: dict) -> dict: + return { + "references": count_list(resources, "references"), + "scripts": count_list(resources, "scripts"), + "assets": count_list(resources, "assets"), + "reports": count_list(resources, "reports"), + } + + +def eval_counts(eval_plan: dict) -> dict: + return { + "trigger": count_list(eval_plan, "trigger"), + "output": count_list(eval_plan, "output"), + "adversarial": count_list(eval_plan, "adversarial"), + "baseline": 1 if eval_plan.get("baseline") else 0, + } + + +def build_semantic_contract( + *, + skill_dir: Path, + platform: str, + frontmatter: dict, + interface: dict, + compatibility: dict, + manifest: dict, + ir: dict, + ir_source: str, +) -> dict: + trigger_surface = ir.get("trigger_surface", {}) if isinstance(ir.get("trigger_surface"), dict) else {} + workflow = ir.get("workflow", {}) if isinstance(ir.get("workflow"), dict) else {} + resources = ir.get("resources", {}) if isinstance(ir.get("resources"), dict) else {} + eval_plan = ir.get("eval_plan", {}) if isinstance(ir.get("eval_plan"), dict) else {} + + frontmatter_name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name) + frontmatter_description = str(frontmatter.get("description") or "") + ir_name = str(ir.get("name") or "") if ir else "" + ir_description = str(trigger_surface.get("description") or "") if ir else "" + name = ir_name or frontmatter_name + description = ir_description or frontmatter_description + job = str(ir.get("job_to_be_done") or description) + targets = ir.get("targets") if isinstance(ir.get("targets"), list) else compatibility.get("adapter_targets", []) + target_values = [str(item) for item in targets] + adapter_targets = [str(item) for item in compatibility.get("adapter_targets", [])] + + semantic_contract = { + "name": name, + "description": description, + "job_to_be_done": job, + "trigger_description": description, + "should_trigger_count": count_list(trigger_surface, "should_trigger"), + "should_not_trigger_count": count_list(trigger_surface, "should_not_trigger"), + "edge_case_count": count_list(trigger_surface, "edge_cases"), + "workflow_step_count": count_list(workflow, "steps"), + "decision_point_count": count_list(workflow, "decision_points"), + "failure_mode_count": count_list(workflow, "failure_modes"), + "resource_counts": resource_counts(resources), + "eval_counts": eval_counts(eval_plan), + "risk": ir.get("risk", {}) if isinstance(ir.get("risk"), dict) else {}, + "governance": ir.get("governance", {}) if isinstance(ir.get("governance"), dict) else {}, + "targets": target_values, + "source_files_count": count_list(ir, "source_files") if ir else 0, + } + alias_declared = ( + platform in {"agent-skills", "vscode"} and "agent-skills-compatible" in target_values + ) + semantic_parity = { + "source": "skill-ir" if ir else "frontmatter-fallback", + "ir_source": ir_source, + "name_matches_ir": bool(ir) and frontmatter_name == name, + "description_matches_ir": bool(ir) and frontmatter_description == description, + "platform_declared_in_ir": platform in target_values + or (platform == "generic" and "agent-skills-compatible" in target_values) + or alias_declared, + "platform_declared_in_interface": platform in adapter_targets, + "display_name_present": bool(interface.get("display_name")), + "default_prompt_present": bool(interface.get("default_prompt")), + } + return { + "name": name, + "description": description, + "job_to_be_done": job, + "ir_source": ir_source, + "ir_schema_version": str(ir.get("schema_version") or "none"), + "semantic_contract": semantic_contract, + "semantic_parity": semantic_parity, + } + + +def build_manifest(skill_dir: Path, platform: str) -> dict: + frontmatter = read_frontmatter(skill_dir / "SKILL.md") + manifest = read_json(skill_dir / "manifest.json") + interface_doc = read_interface(skill_dir) + interface = interface_doc.get("interface", {}) + compatibility = interface_doc.get("compatibility", {}) + activation = compatibility.get("activation", {}) + execution = compatibility.get("execution", {}) + trust = compatibility.get("trust", {}) + degradation = compatibility.get("degradation", {}) + require_fields(frontmatter, ["name", "description"], "frontmatter") + require_fields(interface, ["display_name", "short_description", "default_prompt"], "interface") + require_fields(compatibility, ["canonical_format", "adapter_targets"], "compatibility") + require_fields(activation, ["mode"], "compatibility.activation") + require_fields(execution, ["context", "shell"], "compatibility.execution") + require_fields( + trust, + ["source_tier", "remote_inline_execution", "remote_metadata_policy"], + "compatibility.trust", + ) + require_target_degradation(degradation, compatibility.get("adapter_targets", [])) + ir, ir_source = find_skill_ir(skill_dir, str(frontmatter.get("name") or manifest.get("name") or skill_dir.name)) + semantic = build_semantic_contract( + skill_dir=skill_dir, + platform=platform, + frontmatter=frontmatter, + interface=interface, + compatibility=compatibility, + manifest=manifest, + ir=ir, + ir_source=ir_source, + ) + compiled = compile_target_contract(skill_dir, platform) + if compiled.get("failures"): + raise ValueError(f"Compiler failed for {platform}: {'; '.join(compiled['failures'])}") + return { + "name": semantic["name"], + "description": semantic["description"], + "version": manifest.get("version") or frontmatter.get("version", "1.0.0"), + "platform": platform, + "skill_root": semantic["name"], + "job_to_be_done": semantic["job_to_be_done"], + "ir_source": semantic["ir_source"], + "ir_schema_version": semantic["ir_schema_version"], + "semantic_contract": semantic["semantic_contract"], + "semantic_parity": semantic["semantic_parity"], + "compiler": compiled["compiler"], + "compiled_contract": compiled["compiled_contract"], + "permission_contract": compiled["permission_contract"], + "target_permission_contract": compiled["target_permission_contract"], + "target_native_contract": compiled["target_native_contract"], + "target_transform": compiled["target_transform"], + "unsupported_features": compiled["unsupported_features"], + "compiler_warnings": compiled["warnings"], + "display_name": interface.get("display_name", skill_dir.name), + "short_description": interface.get("short_description", ""), + "default_prompt": interface.get("default_prompt", ""), + "canonical_metadata": "agents/interface.yaml", + "canonical_format": compatibility.get("canonical_format", "agent-skills"), + "adapter_targets": compatibility.get("adapter_targets", []), + "activation_mode": activation.get("mode", "manual"), + "activation_paths": activation.get("paths", []), + "execution_context": execution.get("context", "inline"), + "shell": execution.get("shell", "bash"), + "trust_level": trust.get("source_tier", "local"), + "remote_inline_execution": trust.get("remote_inline_execution", "forbid"), + "remote_metadata_policy": trust.get("remote_metadata_policy", "allow-metadata-only"), + "degradation_strategy": degradation.get(platform, "neutral-source"), + "portability_profile": { + "activation_mode": activation.get("mode", "manual"), + "activation_paths": activation.get("paths", []), + "execution_context": execution.get("context", "inline"), + "shell": execution.get("shell", "bash"), + "source_tier": trust.get("source_tier", "local"), + "remote_inline_execution": trust.get("remote_inline_execution", "forbid"), + "remote_metadata_policy": trust.get("remote_metadata_policy", "allow-metadata-only"), + "degradation_strategy": degradation.get(platform, "neutral-source"), + }, + } + + +EXCLUDED_ARCHIVE_PARTS = {".git", ".previews", "__pycache__", ".venv", "venv", "node_modules", "dist"} + + +def should_skip_archive_path(rel_path: Path) -> bool: + parts = rel_path.parts + if any(part in EXCLUDED_ARCHIVE_PARTS for part in parts): + return True + if rel_path.name == "SKILL.md" and parts != ("SKILL.md",): + return True + if parts == ("reports", "telemetry_events.jsonl"): + return True + if parts and parts[0] == "tests" and any(part.startswith("tmp") for part in parts[1:]): + return True + return False + + +def is_relative_to(path: Path, parent: Path) -> bool: + try: + path.relative_to(parent) + return True + except ValueError: + return False + + +def write_yaml_file(path: Path, payload: dict) -> None: + path.write_text( + yaml.safe_dump(payload, sort_keys=False, allow_unicode=True), + encoding="utf-8", + ) + + +def safe_output_dir(skill_dir: Path, requested_output_dir: Path, cwd: Path) -> Path: + skill_dir = skill_dir.resolve() + cwd = cwd.resolve() + requested_output_dir = requested_output_dir.expanduser() + candidate = requested_output_dir if requested_output_dir.is_absolute() else cwd / requested_output_dir + if candidate.is_symlink(): + raise ValueError(f"Refusing symlink output directory: {candidate}") + out_dir = candidate.resolve() + home = Path.home().resolve() + filesystem_root = Path(out_dir.anchor).resolve() + + dangerous_exact = {filesystem_root, home, cwd, skill_dir, skill_dir.parent.resolve()} + if out_dir in dangerous_exact or out_dir in skill_dir.parents: + raise ValueError(f"Refusing dangerous output directory: {out_dir}") + if out_dir.exists() and not out_dir.is_dir(): + raise ValueError(f"Output path exists but is not a directory: {out_dir}") + if not (is_relative_to(out_dir, cwd) or is_relative_to(out_dir, skill_dir)): + raise ValueError(f"Output directory must stay under the current workspace or skill directory: {out_dir}") + return out_dir + + +def write_adapter(skill_dir: Path, out_dir: Path, platform: str) -> Path: + target_dir = out_dir / "targets" / platform + target_dir.mkdir(parents=True, exist_ok=True) + if platform not in PLATFORM_CONTRACTS: + raise ValueError(f"Unsupported platform: {platform}") + payload = build_manifest(skill_dir, platform) + if platform == "openai": + meta_dir = target_dir / "agents" + meta_dir.mkdir(parents=True, exist_ok=True) + write_yaml_file( + meta_dir / "openai.yaml", + { + "interface": { + "display_name": payload["display_name"], + "short_description": payload["short_description"], + "default_prompt": payload["default_prompt"], + }, + "compatibility": { + "canonical_format": payload["canonical_format"], + "activation_mode": payload["activation_mode"], + "execution_context": payload["execution_context"], + "shell": payload["shell"], + "trust_level": payload["trust_level"], + "remote_inline_execution": payload["remote_inline_execution"], + "permission_contract": { + "review_required": payload["target_permission_contract"]["review_required"], + "declared_capabilities": payload["target_permission_contract"]["declared_capabilities"], + "native_enforcement": payload["target_permission_contract"]["native_enforcement"], + "representation": payload["target_permission_contract"]["representation"], + }, + "native_contract": { + "native_surface": payload["target_native_contract"]["native_surface"], + "activation_policy": payload["target_native_contract"]["activation"]["policy"], + "resource_strategy": payload["target_native_contract"]["resources"]["strategy"], + "permission_enforcement": payload["target_native_contract"]["permissions"]["enforcement"], + "review_artifacts": payload["target_native_contract"]["review"]["artifacts"], + }, + "degradation_strategy": payload["degradation_strategy"], + }, + }, + ) + payload["install_hint"] = f"Use the packaged skill and include targets/openai/agents/openai.yaml when the client expects OpenAI-style interface metadata." + elif platform == "claude": + notes = target_dir / "README.md" + native = payload["target_native_contract"] + notes.write_text( + f"# Claude-Compatible Package\n\nUse `{payload['name']}` with its neutral source files. This target does not require vendor metadata by default.\n\n" + f"Native surface: {native['native_surface']}.\n\n" + f"Activation: {native['activation']['policy']}\n\n" + f"Resources: {native['resources']['strategy']}\n\n" + f"Scripts: {native['scripts']['strategy']}\n\n" + f"Permission metadata is preserved in `adapter.json` under `target_permission_contract` and `target_native_contract` for reviewer visibility.\n", + encoding="utf-8", + ) + payload["install_hint"] = f"Use the packaged skill directly; this target relies on SKILL.md and optional neutral metadata." + elif platform == "vscode": + notes = target_dir / "README.md" + native = payload["target_native_contract"] + notes.write_text( + f"# VS Code / Copilot Agent Skills Package\n\n" + f"Install `{payload['name']}` as a VS Code user or project scoped Agent Skill. Keep the folder name aligned with `SKILL.md` frontmatter name.\n\n" + f"Native surface: {native['native_surface']}.\n\n" + f"Activation: {native['activation']['policy']}\n\n" + f"Resources: {native['resources']['strategy']}\n\n" + f"Scripts: {native['scripts']['strategy']}\n\n" + f"Permission model: {payload['target_permission_contract']['permission_model']}. " + "Review `target_permission_contract`, workspace trust, and `reports/security_trust_report.md` before running scripts.\n\n" + "This adapter does not perform automatic VS Code installation; it preserves the reviewed source package plus install notes.\n", + encoding="utf-8", + ) + payload["install_hint"] = ( + "Install the package as a VS Code user or project scoped Agent Skill; use targets/vscode/README.md for scope and trust notes." + ) + else: + payload["install_hint"] = f"Use {payload['name']} as an Agent Skills compatible package." + path = target_dir / "adapter.json" + payload["contract"] = PLATFORM_CONTRACTS.get(platform, PLATFORM_CONTRACTS["generic"]) + path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + return path + + +def make_zip(skill_dir: Path, out_dir: Path, package_name: str) -> Path: + zip_path = out_dir / f"{package_name}.zip" + skill_root = skill_dir.resolve() + out_root = out_dir.resolve() + with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf: + for path in skill_dir.rglob("*"): + if path.is_symlink() or not path.is_file(): + continue + resolved = path.resolve() + if not is_relative_to(resolved, skill_root): + continue + if is_relative_to(resolved, out_root): + continue + rel_path = path.relative_to(skill_dir) + if should_skip_archive_path(rel_path): + continue + zf.write(path, arcname=str(PurePosixPath(package_name, *rel_path.parts))) + return zip_path + + +def copy_manifest(skill_dir: Path, out_dir: Path) -> tuple[Path, str]: + manifest_path = out_dir / "manifest.json" + manifest = build_manifest(skill_dir, "generic") + manifest_path.write_text( + json.dumps(manifest, ensure_ascii=False, indent=2), + encoding="utf-8", + ) + return manifest_path, package_name_from_manifest(manifest, skill_dir) + + +def load_expectations(path: Path | None) -> dict: + if path is None: + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def validate_exports(out_dir: Path, expectations: dict) -> dict: + failures = [] + required_targets = expectations.get("required_targets", []) + required_fields = expectations.get("required_fields", []) + required_by_target = { + key[: -len("_required_files")]: value + for key, value in expectations.items() + if key.endswith("_required_files") + } + + for target in required_targets: + adapter_path = out_dir / "targets" / target / "adapter.json" + if not adapter_path.exists(): + failures.append(f"missing adapter for target: {target}") + continue + payload = json.loads(adapter_path.read_text(encoding="utf-8")) + for field in required_fields: + if field not in payload: + failures.append(f"missing field '{field}' in {adapter_path.relative_to(out_dir)}") + for rel in required_by_target.get(target, []): + if not (out_dir / rel).exists(): + failures.append(f"missing file: {rel}") + return {"ok": not failures, "failures": failures} + + +def main() -> None: + parser = argparse.ArgumentParser(description="Generate lightweight cross-platform packaging artifacts.") + parser.add_argument("skill_dir", help="Path to the skill directory") + parser.add_argument("--platform", action="append", default=[], help="Target platform: openai, claude, generic, vscode") + parser.add_argument("--output-dir", default="dist", help="Output directory") + parser.add_argument("--expectations", help="JSON file describing packaging expectations") + parser.add_argument("--zip", action="store_true", help="Create a zip package") + args = parser.parse_args() + + generated = [] + failures = [] + try: + skill_dir = Path(args.skill_dir).resolve() + out_dir = safe_output_dir(skill_dir, Path(args.output_dir), Path.cwd()) + if out_dir.exists(): + shutil.rmtree(out_dir) + out_dir.mkdir(parents=True) + manifest, package_name = copy_manifest(skill_dir, out_dir) + generated.append(str(manifest)) + for platform in (args.platform or ["generic"]): + generated.append(str(write_adapter(skill_dir, out_dir, platform))) + if args.zip: + generated.append(str(make_zip(skill_dir, out_dir, package_name))) + except (FileNotFoundError, ValueError, yaml.YAMLError) as exc: + failures.append(str(exc)) + + expectations = load_expectations(Path(args.expectations).resolve()) if args.expectations else {} + validation = validate_exports(out_dir, expectations) if expectations and not failures else None + report = { + "output_dir": str(out_dir) if "out_dir" in locals() else str(Path(args.output_dir).resolve()), + "generated": generated, + "contracts": PLATFORM_CONTRACTS, + "validation": validation, + "failure_handling": { + "missing_required_file": "exit with code 2 when expectations are provided and validation fails", + "missing_required_field": "exit with code 2 when expectations are provided and validation fails", + "invalid_yaml_or_frontmatter": "exit with code 2 when parsing fails", + "unsupported_platform": "exit with code 2 when the platform is not defined in PLATFORM_CONTRACTS", + }, + "failures": failures, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + if failures or (validation and not validation["ok"]): + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/cross_packager_contracts.py b/scripts/cross_packager_contracts.py new file mode 100644 index 0000000..f5c2b8b --- /dev/null +++ b/scripts/cross_packager_contracts.py @@ -0,0 +1,107 @@ +"""Platform contract definitions for cross-platform skill packages.""" + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by cross_packager.py to keep platform contract data separate from packaging flow." + + +COMMON_REQUIRED_FIELDS = [ + "name", + "description", + "version", + "display_name", + "short_description", + "default_prompt", + "job_to_be_done", + "ir_source", + "ir_schema_version", + "semantic_contract", + "semantic_parity", + "canonical_metadata", + "canonical_format", + "activation_mode", + "execution_context", + "shell", + "trust_level", + "remote_inline_execution", + "degradation_strategy", + "portability_profile", + "permission_contract", + "target_permission_contract", + "target_native_contract", +] + +STANDARD_FIELD_MAPPING = { + "display_name": "adapter.display_name", + "short_description": "adapter.short_description", + "default_prompt": "adapter.default_prompt", + "execution_context": "compatibility.execution.context", + "shell": "compatibility.execution.shell", +} + + +def interface_field_mapping() -> dict[str, str]: + return { + "display_name": "interface.display_name", + "short_description": "interface.short_description", + "default_prompt": "interface.default_prompt", + "execution_context": "compatibility.execution.context", + "shell": "compatibility.execution.shell", + } + + +PLATFORM_CONTRACTS = { + "openai": { + "required_fields": list(COMMON_REQUIRED_FIELDS), + "required_files": ["targets/openai/adapter.json", "targets/openai/agents/openai.yaml"], + "field_mapping": interface_field_mapping(), + }, + "claude": { + "required_fields": list(COMMON_REQUIRED_FIELDS), + "required_files": ["targets/claude/adapter.json", "targets/claude/README.md"], + "field_mapping": dict(STANDARD_FIELD_MAPPING), + }, + "generic": { + "required_fields": list(COMMON_REQUIRED_FIELDS), + "required_files": ["targets/generic/adapter.json"], + "field_mapping": dict(STANDARD_FIELD_MAPPING), + }, + "vscode": { + "required_fields": [ + "name", + "description", + "version", + "display_name", + "short_description", + "default_prompt", + "job_to_be_done", + "ir_source", + "ir_schema_version", + "semantic_contract", + "semantic_parity", + "compiler", + "compiled_contract", + "permission_contract", + "target_permission_contract", + "target_native_contract", + "target_transform", + "canonical_metadata", + "canonical_format", + "activation_mode", + "execution_context", + "shell", + "trust_level", + "remote_inline_execution", + "degradation_strategy", + "portability_profile", + ], + "required_files": ["targets/vscode/adapter.json", "targets/vscode/README.md"], + "field_mapping": { + "name": "SKILL.md::frontmatter.name and folder name", + "description": "SKILL.md::frontmatter.description", + "display_name": "agents/interface.yaml::interface.display_name", + "execution_context": "compatibility.execution.context", + "permissions": "adapter.target_permission_contract", + }, + }, +} diff --git a/scripts/description_optimizer_reporting.py b/scripts/description_optimizer_reporting.py new file mode 100644 index 0000000..5889c0b --- /dev/null +++ b/scripts/description_optimizer_reporting.py @@ -0,0 +1,141 @@ +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by optimize_description.py to render description optimization reports." + + +def render_markdown(report: dict, title: str) -> str: + lines = [ + f"# {title}", + "", + f"Winner: `{report['winner']['label']}`", + "", + f"- current tokens: `{report['current_candidate']['estimated_tokens']}`", + f"- winner tokens: `{report['winner']['estimated_tokens']}`", + ] + if report["baseline"]: + lines.append(f"- baseline tokens: `{report['baseline']['estimated_tokens']}`") + lines.extend( + [ + "", + "## Winner", + "", + report["winner"]["description"], + "", + "## Candidate Ranking", + "", + "| Candidate | Tokens | Dev FP | Dev FN | Dev Near | Holdout FP | Holdout FN |", + "| --- | ---: | ---: | ---: | ---: | ---: | ---: |", + ] + ) + for candidate in report["candidates"]: + holdout = candidate.get("holdout", {}) + lines.append( + f"| `{candidate['label']}` | {candidate['estimated_tokens']} | {candidate['dev']['false_positives']} | {candidate['dev']['false_negatives']} | {candidate['dev']['near_neighbor_pass_rate']} | {holdout.get('false_positives', '-')} | {holdout.get('false_negatives', '-')} |" + ) + + lines.extend( + [ + "", + "## Acceptance Gates", + "", + "| Gate | Winner FP | Winner FN | Current FP | Current FN | Baseline FP | Baseline FN |", + "| --- | ---: | ---: | ---: | ---: | ---: | ---: |", + ] + ) + for gate_name, gate in ( + ("Holdout", report["acceptance_gates"]["holdout_non_regression"]), + ("Blind Holdout", report["acceptance_gates"]["blind_holdout_non_regression"]), + ("Judge Blind Holdout", report["acceptance_gates"]["judge_blind_holdout_non_regression"]), + ("Adversarial Holdout", report["acceptance_gates"]["adversarial_holdout_non_regression"]), + ): + winner_gate = gate.get("winner") or {} + current_gate = gate.get("current") or {} + baseline_gate = gate.get("baseline") or {} + if not winner_gate and not current_gate and not baseline_gate: + continue + lines.append( + f"| {gate_name} | {winner_gate.get('false_positives', '-')} | {winner_gate.get('false_negatives', '-')} | {current_gate.get('false_positives', '-')} | {current_gate.get('false_negatives', '-')} | {baseline_gate.get('false_positives', '-')} | {baseline_gate.get('false_negatives', '-')} |" + ) + + lines.extend( + [ + "", + "## Calibration", + "", + "| Gate | Winner Gap | Winner Risk | Winner Boundary Rate | Current Gap | Baseline Gap |", + "| --- | ---: | --- | ---: | ---: | ---: |", + ] + ) + for gate_name, gate in ( + ("Holdout", report["acceptance_gates"]["holdout_non_regression"]), + ("Blind Holdout", report["acceptance_gates"]["blind_holdout_non_regression"]), + ("Judge Blind Holdout", report["acceptance_gates"]["judge_blind_holdout_non_regression"]), + ("Adversarial Holdout", report["acceptance_gates"]["adversarial_holdout_non_regression"]), + ): + winner_calibration = gate.get("winner_calibration") or {} + current_calibration = gate.get("current_calibration") or {} + baseline_calibration = gate.get("baseline_calibration") or {} + if not winner_calibration and not current_calibration and not baseline_calibration: + continue + lines.append( + f"| {gate_name} | {winner_calibration.get('score_gap', '-')} | {winner_calibration.get('risk_band', '-')} | {winner_calibration.get('boundary_case_rate', '-')} | {current_calibration.get('score_gap', '-')} | {baseline_calibration.get('score_gap', '-')} |" + ) + + lines.extend( + [ + "", + "## Judge Blind Summary", + "", + "| Gate | Winner Agreement | Winner Mean Confidence | Current Agreement | Baseline Agreement |", + "| --- | ---: | ---: | ---: | ---: |", + ] + ) + judge_gate = report["acceptance_gates"]["judge_blind_holdout_non_regression"] + judge_winner = (judge_gate.get("winner") or {}).get("judge_summary") or {} + judge_current = (judge_gate.get("current") or {}).get("judge_summary") or {} + judge_baseline = (judge_gate.get("baseline") or {}).get("judge_summary") or {} + if judge_winner or judge_current or judge_baseline: + lines.append( + f"| Judge Blind Holdout | {judge_winner.get('agreement_rate', '-')} | {judge_winner.get('mean_confidence', '-')} | {judge_current.get('agreement_rate', '-')} | {judge_baseline.get('agreement_rate', '-')} |" + ) + + lines.extend( + [ + "", + "## Family Health", + "", + "| Gate | Winner Clean Families | Winner Weakest Family | Current Clean Families | Baseline Clean Families |", + "| --- | --- | --- | --- | --- |", + ] + ) + for gate_name, gate in ( + ("Holdout", report["acceptance_gates"]["holdout_non_regression"]), + ("Blind Holdout", report["acceptance_gates"]["blind_holdout_non_regression"]), + ("Judge Blind Holdout", report["acceptance_gates"]["judge_blind_holdout_non_regression"]), + ("Adversarial Holdout", report["acceptance_gates"]["adversarial_holdout_non_regression"]), + ): + winner_health = gate.get("winner_family_health") or {} + current_health = gate.get("current_family_health") or {} + baseline_health = gate.get("baseline_family_health") or {} + if not winner_health and not current_health and not baseline_health: + continue + weakest = winner_health.get("weakest_family") or {} + weakest_label = ( + f"{weakest.get('family')} ({weakest.get('errors')} errors)" + if weakest.get("family") + else "-" + ) + lines.append( + f"| {gate_name} | {winner_health.get('clean_family_count', '-')}/{winner_health.get('family_count', '-')} | {weakest_label} | {current_health.get('clean_family_count', '-')}/{current_health.get('family_count', '-')} | {baseline_health.get('clean_family_count', '-')}/{baseline_health.get('family_count', '-')} |" + ) + + lines.extend( + [ + "", + "## Selection Logic", + "", + "Ordered by:", + ] + ) + for item in report["selection_logic"]["priority"]: + lines.append(f"- {item}") + return "\n".join(lines) + "\n" diff --git a/scripts/diff_eval.py b/scripts/diff_eval.py new file mode 100644 index 0000000..65bffd0 --- /dev/null +++ b/scripts/diff_eval.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +def main() -> None: + parser = argparse.ArgumentParser(description="Diff two eval reports.") + parser.add_argument("before") + parser.add_argument("after") + args = parser.parse_args() + + before = json.loads(Path(args.before).read_text(encoding="utf-8")) + after = json.loads(Path(args.after).read_text(encoding="utf-8")) + diff = { + "false_positive_delta": after["summary"]["false_positives"] - before["summary"]["false_positives"], + "false_negative_delta": after["summary"]["false_negatives"] - before["summary"]["false_negatives"], + "precision_delta": round(after["summary"]["average_precision"] - before["summary"]["average_precision"], 3), + "recall_delta": round(after["summary"]["average_recall"] - before["summary"]["average_recall"], 3), + } + print(json.dumps(diff, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/emit_telemetry_event.py b/scripts/emit_telemetry_event.py new file mode 100644 index 0000000..d737703 --- /dev/null +++ b/scripts/emit_telemetry_event.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +import argparse +import json +import shlex +from pathlib import Path +from typing import Any + +from render_adoption_drift_report import ( + ALLOWED_ACTIVATION_TYPES, + ALLOWED_EVENTS, + ALLOWED_FAILURE_TYPES, + ALLOWED_OUTCOMES, + ALLOWED_SOURCES, + display_path, + normalize_event, + skill_defaults, +) + + +def default_spool_path(skill_dir: Path) -> Path: + return skill_dir / ".yao" / "telemetry_spool" / "external_events.jsonl" + + +def append_event(path: Path, event: dict[str, str]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a", encoding="utf-8") as handle: + handle.write(json.dumps(event, ensure_ascii=False, sort_keys=True) + "\n") + + +def import_command(skill_dir: Path, output_jsonl: Path) -> str: + return shlex.join( + [ + "python3", + "scripts/yao.py", + "telemetry-import", + display_path(skill_dir), + "--input-jsonl", + display_path(output_jsonl), + ] + ) + + +def emit_event( + skill_dir: Path, + output_jsonl: Path | None = None, + event_name: str = "script_run", + activation_type: str = "manual", + outcome: str = "unknown", + failure_type: str = "none", + source: str = "external", + command: str = "external-client", + timestamp: str | None = None, + skill_name: str | None = None, + version: str | None = None, + dry_run: bool = False, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + output_jsonl = (output_jsonl or default_spool_path(skill_dir)).resolve() + raw_event: dict[str, Any] = { + "event": event_name, + "activation_type": activation_type, + "outcome": outcome, + "failure_type": failure_type, + "source": source, + "command": command, + } + if timestamp: + raw_event["timestamp"] = timestamp + if skill_name: + raw_event["skill"] = skill_name + if version: + raw_event["version"] = version + event, failures = normalize_event(raw_event, skill_defaults(skill_dir), "emit") + if event and not failures and not dry_run: + append_event(output_jsonl, event) + return { + "ok": not failures, + "schema_version": "1.0", + "skill_dir": display_path(skill_dir), + "output_jsonl": display_path(output_jsonl), + "dry_run": dry_run, + "emitted": bool(event and not failures and not dry_run), + "event": event or {}, + "failures": failures, + "artifacts": { + "spool_jsonl": display_path(output_jsonl), + "import_command": import_command(skill_dir, output_jsonl), + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Emit one metadata-only telemetry event for later import.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-jsonl") + parser.add_argument("--event", choices=sorted(ALLOWED_EVENTS), default="script_run") + parser.add_argument("--activation-type", choices=sorted(ALLOWED_ACTIVATION_TYPES), default="manual") + parser.add_argument("--outcome", choices=sorted(ALLOWED_OUTCOMES), default="unknown") + parser.add_argument("--failure-type", choices=sorted(ALLOWED_FAILURE_TYPES), default="none") + parser.add_argument("--source", choices=sorted(ALLOWED_SOURCES), default="external") + parser.add_argument("--command", default="external-client") + parser.add_argument("--timestamp") + parser.add_argument("--skill-name") + parser.add_argument("--version") + parser.add_argument("--dry-run", action="store_true") + args = parser.parse_args() + + report = emit_event( + Path(args.skill_dir), + output_jsonl=Path(args.output_jsonl) if args.output_jsonl else None, + event_name=args.event, + activation_type=args.activation_type, + outcome=args.outcome, + failure_type=args.failure_type, + source=args.source, + command=args.command, + timestamp=args.timestamp, + skill_name=args.skill_name, + version=args.version, + dry_run=args.dry_run, + ) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/evidence_consistency_artifact_roles.py b/scripts/evidence_consistency_artifact_roles.py new file mode 100644 index 0000000..12e82fe --- /dev/null +++ b/scripts/evidence_consistency_artifact_roles.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +"""Compare artifact-role handoffs across world-class evidence reports.""" + +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_evidence_consistency.py to compare preflight and Review Studio artifact-role contracts." + + +def _compare_check( + *, + key: str, + label: str, + expected: Any, + actual: Any, + paths: list[str], + detail: str, +) -> dict[str, Any]: + return { + "key": key, + "label": label, + "status": "pass" if expected == actual else "fail", + "expected": expected, + "actual": actual, + "paths": paths, + "detail": detail, + } + + +def role_contract_signature(contract: dict[str, Any]) -> dict[str, Any]: + roles = { + str(item.get("role", "")): item + for item in contract.get("roles", []) + if isinstance(item, dict) and str(item.get("role", "")).strip() + } + return { + "role_source": contract.get("role_source"), + "counts_as_evidence": contract.get("counts_as_evidence"), + "artifact_prefill_counts_as_evidence": contract.get("artifact_prefill_counts_as_evidence"), + "submission_ref_total_count": contract.get("submission_ref_total_count"), + "submission_ref_ready_count": contract.get("submission_ref_ready_count"), + "supporting_evidence_total_count": contract.get("supporting_evidence_total_count"), + "supporting_evidence_ready_count": contract.get("supporting_evidence_ready_count"), + "submission_ref_copy_to_artifact_refs": roles.get("submission-ref", {}).get("copy_to_artifact_refs"), + "supporting_evidence_copy_to_artifact_refs": roles.get("supporting-evidence", {}).get( + "copy_to_artifact_refs" + ), + } + + +def preflight_role_signatures(world_class_preflight: dict[str, Any]) -> dict[str, dict[str, Any]]: + signatures: dict[str, dict[str, Any]] = {} + items = world_class_preflight.get("items", []) if isinstance(world_class_preflight, dict) else [] + for item in items: + if not isinstance(item, dict): + continue + key = str(item.get("evidence_key", "")).strip() + submission_kit = item.get("submission_kit", {}) if isinstance(item.get("submission_kit", {}), dict) else {} + contract = ( + submission_kit.get("artifact_role_contract", {}) + if isinstance(submission_kit.get("artifact_role_contract", {}), dict) + else {} + ) + if key and contract: + signatures[key] = role_contract_signature(contract) + return signatures + + +def review_studio_role_signatures(review_studio: dict[str, Any]) -> dict[str, dict[str, Any]]: + actions = review_studio.get("review_actions", []) if isinstance(review_studio, dict) else [] + for action in actions: + if not isinstance(action, dict) or action.get("gate_key") != "world-class-evidence": + continue + signatures: dict[str, dict[str, Any]] = {} + for step in action.get("evidence_steps", []): + if not isinstance(step, dict): + continue + key = str(step.get("key", "")).strip() + contract = ( + step.get("artifact_role_contract", {}) + if isinstance(step.get("artifact_role_contract", {}), dict) + else {} + ) + if key and contract: + signatures[key] = role_contract_signature(contract) + return signatures + return {} + + +def build_preflight_artifact_role_handoff_checks( + *, + skill_dir: Path, + world_class_preflight: dict[str, Any], + review_studio: dict[str, Any], + report_paths: dict[str, str], +) -> list[dict[str, Any]]: + preflight_submissions = ( + world_class_preflight.get("submissions", {}) + if isinstance(world_class_preflight.get("submissions", {}), dict) + else {} + ) + preflight_commands = ( + preflight_submissions.get("commands", {}) + if isinstance(preflight_submissions.get("commands", {}), dict) + else {} + ) + preflight_role_contract = ( + preflight_submissions.get("artifact_role_contract", {}) + if isinstance(preflight_submissions.get("artifact_role_contract", {}), dict) + else {} + ) + preflight_roles = { + str(item.get("role", "")): item for item in preflight_role_contract.get("roles", []) if isinstance(item, dict) + } + default_submissions_dir = "evidence/world_class/submissions" + expected_preflight_handoff = { + "directory": default_submissions_dir, + "drafts_count_as_evidence": False, + "preflight_counts_submission_as_completion": False, + "html_report": "reports/world_class_evidence_preflight.html", + "html_exists": True, + "prepare_submission": f"python3 scripts/yao.py world-class-submission-kit . --output-dir {default_submissions_dir}", + "prepare_prefilled_submission": ( + f"python3 scripts/yao.py world-class-submission-kit . --output-dir {default_submissions_dir} " + "--prefill-artifacts" + ), + "validate_intake": f"python3 scripts/yao.py world-class-intake . --submissions-dir {default_submissions_dir}", + "submission_review": f"python3 scripts/yao.py world-class-submission-review . --submissions-dir {default_submissions_dir}", + "refresh_ledger": f"python3 scripts/yao.py world-class-ledger . --submissions-dir {default_submissions_dir}", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard .", + "artifact_prefill_counts_as_evidence": False, + "artifact_role_source": "world-class-submission-kit", + "artifact_role_counts_as_evidence": False, + "artifact_role_prefill_counts_as_evidence": False, + "submission_ref_role_present": True, + "supporting_evidence_role_present": True, + "submission_ref_copy_to_artifact_refs": True, + "supporting_evidence_copy_to_artifact_refs": False, + "submission_ref_total_present": True, + "supporting_evidence_total_present": True, + } + actual_preflight_handoff = { + "directory": preflight_submissions.get("directory"), + "drafts_count_as_evidence": preflight_submissions.get("drafts_count_as_evidence"), + "preflight_counts_submission_as_completion": preflight_submissions.get( + "preflight_counts_submission_as_completion" + ), + "html_report": world_class_preflight.get("artifacts", {}).get("html") + if isinstance(world_class_preflight.get("artifacts", {}), dict) + else None, + "html_exists": (skill_dir / "reports" / "world_class_evidence_preflight.html").exists(), + "prepare_submission": preflight_commands.get("prepare_submission"), + "prepare_prefilled_submission": preflight_commands.get("prepare_prefilled_submission"), + "validate_intake": preflight_commands.get("validate_intake"), + "submission_review": preflight_commands.get("submission_review"), + "refresh_ledger": preflight_commands.get("refresh_ledger"), + "guard_claim": preflight_commands.get("guard_claim"), + "artifact_prefill_counts_as_evidence": preflight_submissions.get("artifact_prefill_counts_as_evidence"), + "artifact_role_source": preflight_role_contract.get("role_source"), + "artifact_role_counts_as_evidence": preflight_role_contract.get("counts_as_evidence"), + "artifact_role_prefill_counts_as_evidence": preflight_role_contract.get( + "artifact_prefill_counts_as_evidence" + ), + "submission_ref_role_present": "submission-ref" in preflight_roles, + "supporting_evidence_role_present": "supporting-evidence" in preflight_roles, + "submission_ref_copy_to_artifact_refs": preflight_roles.get("submission-ref", {}).get( + "copy_to_artifact_refs" + ), + "supporting_evidence_copy_to_artifact_refs": preflight_roles.get("supporting-evidence", {}).get( + "copy_to_artifact_refs" + ), + "submission_ref_total_present": int(preflight_role_contract.get("submission_ref_total_count", 0)) > 0, + "supporting_evidence_total_present": int(preflight_role_contract.get("supporting_evidence_total_count", 0)) + > 0, + } + return [ + _compare_check( + key="preflight-submission-kit-handoff", + label="Preflight exposes a safe submission-kit handoff", + expected=expected_preflight_handoff, + actual=actual_preflight_handoff, + paths=[report_paths["world_class_preflight"], "reports/world_class_evidence_preflight.html"], + detail=( + "Preflight must give operators the exact draft, SHA-prefill, intake, review, ledger, " + "and claim-guard commands without letting drafts, prefill, or submissions count as accepted evidence." + ), + ), + _compare_check( + key="review-studio-preflight-artifact-role-handoff", + label="Review Studio mirrors preflight artifact roles", + expected=preflight_role_signatures(world_class_preflight), + actual=review_studio_role_signatures(review_studio), + paths=[report_paths["world_class_preflight"], report_paths["review_studio"]], + detail=( + "The Review Studio world-class action card must carry the same submission-ref versus " + "supporting-evidence contract as the preflight handoff." + ), + ), + ] diff --git a/scripts/evidence_consistency_core.py b/scripts/evidence_consistency_core.py new file mode 100644 index 0000000..9a0dc61 --- /dev/null +++ b/scripts/evidence_consistency_core.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 +"""Shared helpers for cross-report evidence consistency checks.""" + +import json +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_evidence_consistency.py for shared report loading, comparison, and Markdown rendering helpers." + +REQUIRED_REPORTS = { + "benchmark": "reports/benchmark_reproducibility.json", + "overview": "reports/skill-overview.json", + "interpretation": "reports/skill-interpretation.json", + "adoption": "reports/adoption_drift_report.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "world_class_plan": "reports/world_class_evidence_plan.json", + "world_class_intake": "reports/world_class_evidence_intake.json", + "world_class_preflight": "reports/world_class_evidence_preflight.json", + "world_class_submission_review": "reports/world_class_submission_review.json", + "world_class_operator_runbook": "reports/world_class_operator_runbook.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "review_studio": "reports/review-studio.json", + "package_verification": "reports/package_verification.json", + "install_simulation": "reports/install_simulation.json", + "trust": "reports/security_trust_report.json", + "context_budget": "reports/context_budget.json", + "world_class_claim_guard": "reports/world_class_claim_guard.json", +} +REQUIRED_TEXT_REPORTS = { + "skill_os2_review": "reports/skill-os-2-review.md", +} +BENCHMARK_SUMMARY_KEYS = [ + "release_lock_ready", + "required_artifact_count", + "missing_artifact_count", + "source_contract_sha256", + "archive_sha256", + "world_class_ledger_pending_count", + "world_class_source_check_count", + "world_class_source_pass_count", + "world_class_source_blocked_count", + "beta_test_ready", + "beta_test_blocker_count", + "beta_test_deferred_evidence_count", + "public_claim_ready", + "public_claim_blocker_count", +] +ADOPTION_SUMMARY_KEYS = [ + "event_count", + "adoption_sample_count", + "activation_count", + "accepted_count", + "adoption_rate", + "risk_band", + "event_types", + "source_types", +] +LEDGER_SUMMARY_KEYS = [ + "ledger_entry_count", + "accepted_count", + "pending_count", + "human_pending_count", + "external_pending_count", + "source_check_count", + "source_pass_count", + "source_blocked_count", + "ready_to_claim_world_class", + "decision", +] +LOCKSTEP_SECTIONS = [ + "scorecard", + "capability_profile", + "principle_model", + "contract_boundary", + "quality_review", + "risk_governance", + "world_class_readiness", + "package_assets", + "iteration_roadmap", +] + + +def load_json(path: Path) -> tuple[dict[str, Any], str | None]: + if not path.exists(): + return {}, "missing" + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + return {}, f"invalid-json: {exc}" + if not isinstance(payload, dict): + return {}, "json-root-not-object" + return payload, None + + +def load_text(path: Path) -> tuple[str, str | None]: + if not path.exists(): + return "", "missing" + return path.read_text(encoding="utf-8"), None + + +def rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def nested(payload: dict[str, Any], path: list[str], default: Any = None) -> Any: + current: Any = payload + for key in path: + if not isinstance(current, dict) or key not in current: + return default + current = current[key] + return current + + +def scanned_surface_paths(payload: dict[str, Any]) -> set[str]: + surfaces = payload.get("scanned_surfaces") + if not isinstance(surfaces, list): + return set() + paths: set[str] = set() + for item in surfaces: + if isinstance(item, dict) and isinstance(item.get("path"), str): + paths.add(item["path"]) + elif isinstance(item, str): + paths.add(item) + return paths + + +def as_int(value: Any) -> int | None: + if isinstance(value, bool): + return None + try: + return int(value) + except (TypeError, ValueError): + return None + + +def add_check( + checks: list[dict[str, Any]], + *, + key: str, + label: str, + status: str, + expected: Any, + actual: Any, + paths: list[str], + detail: str, +) -> None: + checks.append( + { + "key": key, + "label": label, + "status": status, + "expected": expected, + "actual": actual, + "paths": paths, + "detail": detail, + } + ) + + +def compare_values( + checks: list[dict[str, Any]], + *, + key: str, + label: str, + expected: Any, + actual: Any, + paths: list[str], + detail: str, +) -> None: + add_check( + checks, + key=key, + label=label, + status="pass" if expected == actual else "fail", + expected=expected, + actual=actual, + paths=paths, + detail=detail, + ) + + +def compare_summary_keys( + checks: list[dict[str, Any]], + *, + key_prefix: str, + label: str, + source_summary: dict[str, Any], + embedded_summary: dict[str, Any], + keys: list[str], + paths: list[str], +) -> None: + expected = {key: source_summary.get(key) for key in keys} + actual = {key: embedded_summary.get(key) for key in keys} + compare_values( + checks, + key=key_prefix, + label=label, + expected=expected, + actual=actual, + paths=paths, + detail="Selected summary fields must match exactly across generated reports.", + ) + + +def gate_by_key(review_studio: dict[str, Any], key: str) -> dict[str, Any]: + gates = review_studio.get("gates") + if not isinstance(gates, list): + return {} + for item in gates: + if isinstance(item, dict) and item.get("key") == key: + return item + return {} + + +def beta_public_claim_split_values( + benchmark: dict[str, Any], + ledger: dict[str, Any], + review_studio: dict[str, Any], +) -> tuple[dict[str, Any], dict[str, Any]]: + benchmark_summary = nested(benchmark, ["summary"], {}) + ledger_summary = nested(ledger, ["summary"], {}) + expected_beta_ready = ( + bool(benchmark_summary.get("reproducibility_ready")) + and bool(benchmark_summary.get("release_lock_ready")) + and bool(benchmark_summary.get("provider_evidence_complete")) + ) + output_review_summary = nested(review_studio, ["data", "output_review_adjudication", "summary"], {}) + review_pair_count = as_int(output_review_summary.get("pair_count")) if isinstance(output_review_summary, dict) else None + review_pending_count = as_int(output_review_summary.get("pending_count")) if isinstance(output_review_summary, dict) else None + expected_human_review_complete = ( + review_pair_count is not None and review_pair_count > 0 and review_pending_count == 0 + ) + ledger_entries = ledger.get("entries", []) if isinstance(ledger, dict) else [] + pending_ledger_keys = sorted( + str(entry.get("key", "")) + for entry in ledger_entries + if isinstance(entry, dict) and entry.get("status") == "pending" and str(entry.get("key", "")) + ) + beta_boundary = { + "beta_test_ready": expected_beta_ready, + "public_claim_ready": ledger_summary.get("ready_to_claim_world_class"), + "human_review_complete": expected_human_review_complete, + "beta_release_ready": expected_beta_ready, + "beta_release_scope": "beta/public test release without superiority, fully-reviewed, or world-class claims", + "deferred_evidence_keys": pending_ledger_keys, + "deferred_human_review": "human-adjudication" in pending_ledger_keys, + } + beta_release = benchmark.get("beta_test_release", {}) if isinstance(benchmark, dict) else {} + deferred_keys = sorted( + str(item.get("key", "")) + for item in beta_release.get("allowed_deferred_evidence", []) + if isinstance(item, dict) and str(item.get("key", "")) + ) + actual_beta_boundary = { + "beta_test_ready": benchmark_summary.get("beta_test_ready") if isinstance(benchmark_summary, dict) else None, + "public_claim_ready": benchmark_summary.get("public_claim_ready") if isinstance(benchmark_summary, dict) else None, + "human_review_complete": benchmark_summary.get("human_review_complete") + if isinstance(benchmark_summary, dict) + else None, + "beta_release_ready": beta_release.get("ready") if isinstance(beta_release, dict) else None, + "beta_release_scope": beta_release.get("scope") if isinstance(beta_release, dict) else None, + "deferred_evidence_keys": deferred_keys, + "deferred_human_review": "human-adjudication" in deferred_keys, + } + return beta_boundary, actual_beta_boundary + + +def report_contract(payload: dict[str, Any]) -> dict[str, Any]: + contract = payload.get("report_contract") + return contract if isinstance(contract, dict) else {} + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Evidence Consistency", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- checks: `{summary['check_count']}`", + f"- pass: `{summary['pass_count']}`", + f"- warn: `{summary['warn_count']}`", + f"- fail: `{summary['fail_count']}`", + "", + "This gate compares generated evidence reports against each other. It does not create provider, human, native-client, or permission-enforcement evidence; it only catches drift between reports that already exist.", + "", + "## Checks", + "", + "| Check | Status | Detail | Paths |", + "| --- | --- | --- | --- |", + ] + for check in report["checks"]: + paths = ", ".join(f"`{path}`" for path in check["paths"]) + lines.append( + "| " + + " | ".join( + [ + check["label"].replace("|", "\\|"), + f"`{check['status']}`", + check["detail"].replace("|", "\\|"), + paths.replace("|", "\\|"), + ] + ) + + " |" + ) + failures = [check for check in report["checks"] if check["status"] == "fail"] + if failures: + lines.extend(["", "## Failures", ""]) + for check in failures: + lines.extend( + [ + f"### {check['label']}", + "", + f"- key: `{check['key']}`", + f"- expected: `{json.dumps(check['expected'], ensure_ascii=False, sort_keys=True)}`", + f"- actual: `{json.dumps(check['actual'], ensure_ascii=False, sort_keys=True)}`", + "", + ] + ) + return "\n".join(lines).rstrip() + "\n" diff --git a/scripts/evidence_consistency_phase_queue.py b/scripts/evidence_consistency_phase_queue.py new file mode 100644 index 0000000..007cc45 --- /dev/null +++ b/scripts/evidence_consistency_phase_queue.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Compare world-class phase queues across generated reports.""" + +from typing import Any + +from evidence_consistency_world_class import world_class_review_action_steps +from world_class_phase_queue import build_phase_queue, summarize_phase_queue + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_evidence_consistency.py to prevent preflight and Review Studio phase-queue drift." + + +def phase_queue_signature(queue: list[dict[str, Any]]) -> list[dict[str, Any]]: + signature: list[dict[str, Any]] = [] + for item in queue: + if not isinstance(item, dict): + continue + signature.append( + { + "phase": item.get("phase"), + "priority": item.get("priority"), + "status": item.get("status"), + "blocked_count": item.get("blocked_count"), + "row_count": item.get("row_count"), + "owners": sorted(str(owner) for owner in item.get("owners", [])), + "evidence_keys": sorted(str(key) for key in item.get("evidence_keys", [])), + "next_action_id": item.get("next_action_id"), + "verification_command": item.get("verification_command"), + "counts_as_completion": item.get("counts_as_completion"), + } + ) + return signature + + +def summary_signature(summary: dict[str, Any]) -> dict[str, Any]: + keys = [ + "phase_queue_count", + "phase_queue_blocked_count", + "phase_queue_row_count", + "phase_queue_next_phase", + "phase_queue_next_action_id", + "phase_queue_next_command", + "phase_queue_counts_as_completion", + ] + return {key: summary.get(key) for key in keys} + + +def keyed_preflight_items(world_class_preflight: dict[str, Any]) -> dict[str, dict[str, Any]]: + items = world_class_preflight.get("items", []) if isinstance(world_class_preflight, dict) else [] + keyed: dict[str, dict[str, Any]] = {} + for item in items: + if not isinstance(item, dict): + continue + key = str(item.get("evidence_key", "")).strip() + if key: + keyed[key] = item + return keyed + + +def keyed_phase_queue_signatures_from_repair_rows(items: dict[str, dict[str, Any]]) -> dict[str, list[dict[str, Any]]]: + signatures: dict[str, list[dict[str, Any]]] = {} + for key, item in items.items(): + rows = item.get("repair_checklist", []) if isinstance(item.get("repair_checklist", []), list) else [] + signatures[key] = phase_queue_signature(build_phase_queue(rows)) + return signatures + + +def keyed_phase_queue_signatures_from_items(items: dict[str, dict[str, Any]], queue_key: str) -> dict[str, list[dict[str, Any]]]: + signatures: dict[str, list[dict[str, Any]]] = {} + for key, item in items.items(): + queue = item.get(queue_key, []) if isinstance(item.get(queue_key, []), list) else [] + signatures[key] = phase_queue_signature(queue) + return signatures + + +def any_phase_queue_counts_as_completion( + *queues: list[dict[str, Any]], + item_maps: dict[str, dict[str, Any]] | None = None, +) -> bool: + item_maps = item_maps or {} + for queue in queues: + for item in queue: + if isinstance(item, dict) and item.get("counts_as_completion") is True: + return True + for item in item_maps.values(): + queue = item.get("phase_queue", []) if isinstance(item.get("phase_queue", []), list) else [] + for row in queue: + if isinstance(row, dict) and row.get("counts_as_completion") is True: + return True + return False + + +def build_phase_queue_consistency_check( + *, + world_class_preflight: dict[str, Any], + world_class_operator_runbook: dict[str, Any], + review_studio: dict[str, Any], + report_paths: dict[str, str], +) -> dict[str, Any]: + repair_rows = ( + world_class_preflight.get("repair_checklist", []) + if isinstance(world_class_preflight.get("repair_checklist", []), list) + else [] + ) + expected_queue = build_phase_queue(repair_rows) + expected_summary = summarize_phase_queue(expected_queue) + preflight_items = keyed_preflight_items(world_class_preflight) + operator_runbook_items = keyed_preflight_items(world_class_operator_runbook) + review_steps = world_class_review_action_steps(review_studio) + expected = { + "summary": summary_signature(expected_summary), + "top_level_phase_queue": phase_queue_signature(expected_queue), + "item_phase_queues": keyed_phase_queue_signatures_from_repair_rows(preflight_items), + "phase_queue_counts_as_completion": False, + } + actual = { + "summary": summary_signature( + world_class_preflight.get("summary", {}) + if isinstance(world_class_preflight.get("summary", {}), dict) + else {} + ), + "top_level_phase_queue": phase_queue_signature( + world_class_preflight.get("phase_queue", []) + if isinstance(world_class_preflight.get("phase_queue", []), list) + else [] + ), + "operator_runbook_summary": summary_signature( + world_class_operator_runbook.get("summary", {}) + if isinstance(world_class_operator_runbook.get("summary", {}), dict) + else {} + ), + "operator_runbook_top_level_phase_queue": phase_queue_signature( + world_class_operator_runbook.get("phase_queue", []) + if isinstance(world_class_operator_runbook.get("phase_queue", []), list) + else [] + ), + "item_phase_queues": keyed_phase_queue_signatures_from_items(preflight_items, "phase_queue"), + "operator_runbook_phase_queues": keyed_phase_queue_signatures_from_items( + operator_runbook_items, + "phase_queue", + ), + "review_studio_phase_queues": keyed_phase_queue_signatures_from_items(review_steps, "phase_queue"), + "phase_queue_counts_as_completion": any_phase_queue_counts_as_completion( + world_class_preflight.get("phase_queue", []) + if isinstance(world_class_preflight.get("phase_queue", []), list) + else [], + world_class_operator_runbook.get("phase_queue", []) + if isinstance(world_class_operator_runbook.get("phase_queue", []), list) + else [], + item_maps={**preflight_items, **operator_runbook_items, **review_steps}, + ), + } + expected["operator_runbook_summary"] = expected["summary"] + expected["operator_runbook_top_level_phase_queue"] = expected["top_level_phase_queue"] + expected["operator_runbook_phase_queues"] = expected["item_phase_queues"] + expected["review_studio_phase_queues"] = expected["item_phase_queues"] + return { + "key": "world-class-phase-queue-consistency", + "label": "World-class phase queues mirror repair rows", + "status": "pass" if expected == actual else "fail", + "expected": expected, + "actual": actual, + "paths": [ + report_paths["world_class_preflight"], + report_paths["world_class_operator_runbook"], + report_paths["review_studio"], + ], + "detail": ( + "Phase queues must be derived from repair rows in preflight and mirrored into the operator runbook " + "and Review Studio without counting queue guidance as completion evidence." + ), + } diff --git a/scripts/evidence_consistency_release.py b/scripts/evidence_consistency_release.py new file mode 100644 index 0000000..72e16c4 --- /dev/null +++ b/scripts/evidence_consistency_release.py @@ -0,0 +1,132 @@ +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_evidence_consistency.py to verify release evidence refresh instructions." + +SOURCE_REFRESH_HEADER = "After source changes that affect scripts" +CLEAN_LOCK_HEADER = "For final release evidence" +CLEAN_LOCK_END = "If `reports/benchmark_reproducibility.json`" + +SOURCE_REFRESH_REPORT_COMMANDS = [ + 'python3 scripts/run_output_execution.py --runner-command \'["python3","scripts/local_output_eval_runner.py"]\'', + 'python3 scripts/compile_skill.py . --generated-at "$GENERATED_AT"', + "python3 scripts/cross_packager.py . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip", + 'python3 scripts/simulate_install.py . --package-dir dist --install-root dist/install-simulation --output-json reports/install_simulation.json --output-md reports/install_simulation.md --generated-at "$GENERATED_AT"', + "python3 scripts/trust_check.py . --output-json reports/security_trust_report.json --output-md reports/security_trust_report.md", + 'python3 scripts/registry_audit.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/verify_package.py . --package-dir dist --expectations evals/packaging_expectations.json --registry-json reports/registry_audit.json --output-json reports/package_verification.json --output-md reports/package_verification.md --require-zip --generated-at "$GENERATED_AT"', + 'python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json --current-package-json reports/registry_audit.json --output-json reports/upgrade_check.json --output-md reports/upgrade_check.md --generated-at "$GENERATED_AT"', + 'python3 scripts/render_adoption_drift_report.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_architecture_maintainability.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/python_compat_check.py . --generated-at "$GENERATED_AT"', + "python3 scripts/probe_runtime_permissions.py . --package-dir dist", + 'python3 scripts/render_review_waivers.py . --generated-at "$GENERATED_AT"', + "python3 scripts/render_review_annotations.py .", + 'python3 scripts/build_skill_atlas.py --workspace-root . --output-dir skill_atlas --report-html reports/skill_atlas.html --report-json reports/skill_atlas.json --today "$GENERATED_AT"', + 'python3 scripts/render_world_class_evidence_plan.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_world_class_evidence_ledger.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_world_class_evidence_intake.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_world_class_submission_review.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_world_class_operator_runbook.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_world_class_claim_guard.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_daily_skillops_report.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_weekly_curator_report.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_skill_os2_audit.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_skill_os2_coverage.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_context_reports.py --generated-at "$GENERATED_AT"', + 'python3 scripts/render_benchmark_reproducibility.py . --generated-at "$GENERATED_AT"', + "python3 scripts/render_skill_overview.py .", + "python3 scripts/render_skill_interpretation.py .", + "python3 scripts/render_review_viewer.py .", + 'python3 scripts/render_world_class_preflight.py . --generated-at "$GENERATED_AT"', + "python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json", + 'python3 scripts/render_evidence_consistency.py . --generated-at "$GENERATED_AT"', +] +CLEAN_LOCK_REPORT_COMMANDS = [ + 'python3 scripts/render_context_reports.py --generated-at "$GENERATED_AT"', + 'python3 scripts/render_benchmark_reproducibility.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_daily_skillops_report.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_weekly_curator_report.py . --generated-at "$GENERATED_AT"', + "python3 scripts/render_skill_overview.py .", + "python3 scripts/render_skill_interpretation.py .", + "python3 scripts/render_review_viewer.py .", + 'python3 scripts/render_world_class_preflight.py . --generated-at "$GENERATED_AT"', + "python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json", + 'python3 scripts/render_evidence_consistency.py . --generated-at "$GENERATED_AT"', +] + + +def section_between(text: str, start: str, end: str) -> str: + if start not in text: + return "" + section = text.split(start, 1)[1] + if end in section: + section = section.split(end, 1)[0] + return section + + +def command_presence(section: str, commands: list[str]) -> dict[str, bool]: + return {command: command in section for command in commands} + + +def build_release_evidence_flow_check(skill_dir: Path) -> dict[str, Any]: + agents_path = skill_dir / "AGENTS.md" + agents_text = agents_path.read_text(encoding="utf-8") if agents_path.exists() else "" + source_refresh = section_between(agents_text, SOURCE_REFRESH_HEADER, CLEAN_LOCK_HEADER) + clean_lock = section_between(agents_text, CLEAN_LOCK_HEADER, CLEAN_LOCK_END) + expected = { + "AGENTS.md": True, + "source_refresh_section": True, + "clean_lock_section": True, + "source_refresh_commands": {command: True for command in SOURCE_REFRESH_REPORT_COMMANDS}, + "clean_lock_commands": {command: True for command in CLEAN_LOCK_REPORT_COMMANDS}, + } + actual = { + "AGENTS.md": agents_path.exists(), + "source_refresh_section": bool(source_refresh), + "clean_lock_section": bool(clean_lock), + "source_refresh_commands": command_presence(source_refresh, SOURCE_REFRESH_REPORT_COMMANDS), + "clean_lock_commands": command_presence(clean_lock, CLEAN_LOCK_REPORT_COMMANDS), + } + return { + "key": "release-evidence-flow-covers-first-class-reports", + "label": "Release evidence flow covers first-class reports", + "status": "pass" if expected == actual else "fail", + "expected": expected, + "actual": actual, + "paths": [ + "AGENTS.md", + "reports/output_execution_runs.json", + "reports/install_simulation.json", + "reports/security_trust_report.json", + "reports/registry_audit.json", + "reports/package_verification.json", + "reports/upgrade_check.json", + "reports/adoption_drift_report.json", + "reports/architecture_maintainability.json", + "reports/python_compatibility.json", + "reports/runtime_permission_probes.json", + "reports/review_waivers.json", + "reports/review_annotations.json", + "reports/skill_atlas.json", + "reports/skill_os2_audit.json", + "reports/skill_os2_coverage.json", + "reports/context_budget.json", + "reports/context_budget_summary.json", + "reports/benchmark_reproducibility.json", + "reports/skill-overview.json", + "reports/skill-interpretation.json", + "reports/review-viewer.json", + "reports/world_class_evidence_preflight.json", + "reports/skillops/daily", + "reports/skillops/weekly", + "reports/review-studio.json", + "reports/evidence_consistency.json", + ], + "detail": ( + "Release refresh and clean-lock instructions must regenerate every first-class report " + "before evidence consistency can be trusted." + ), + } diff --git a/scripts/evidence_consistency_skill_os2_review.py b/scripts/evidence_consistency_skill_os2_review.py new file mode 100644 index 0000000..d483a21 --- /dev/null +++ b/scripts/evidence_consistency_skill_os2_review.py @@ -0,0 +1,83 @@ +import ast +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = ( + "Imported by render_evidence_consistency.py to keep Skill OS 2.0 review summary drift checks " + "out of the main consistency renderer." +) + + +def ci_default_target_count(path: Path) -> int | None: + if not path.exists(): + return None + tree = ast.parse(path.read_text(encoding="utf-8")) + for node in tree.body: + if not isinstance(node, ast.Assign): + continue + if not any(isinstance(target, ast.Name) and target.id == "DEFAULT_TARGETS" for target in node.targets): + continue + if isinstance(node.value, (ast.List, ast.Tuple)): + return len(node.value.elts) + return None + + +def build_skill_os2_review_current_evidence_check( + *, + skill_dir: Path, + skill_os2_review: str, + studio_summary: dict[str, Any], + trust_summary: dict[str, Any], + package_summary: dict[str, Any], + install_summary: dict[str, Any], + benchmark_summary: dict[str, Any], + context_stats: dict[str, Any], + required_text_reports: dict[str, str], + required_reports: dict[str, str], +) -> dict[str, Any]: + ci_target_count = ci_default_target_count(skill_dir / "scripts" / "ci_test.py") + expected_review_snippets = [ + f"score `{studio_summary.get('world_class_score')}`", + f"`{studio_summary.get('gate_count')}` gates", + f"`{studio_summary.get('warning_count')}` warnings", + f"`{trust_summary.get('internal_module_count')}` declared internal modules", + ( + f"`{trust_summary.get('help_smoke_checked_count')} / {trust_summary.get('help_smoke_checked_count')}` " + f"CLI help smoke checks passing across `{trust_summary.get('script_count')}` scripts" + ), + f"`{package_summary.get('archive_entry_count')}` zip entries", + f"archive with `{package_summary.get('archive_entry_count')}` entries", + f"`{install_summary.get('installer_permission_enforced_count')}` installer permission checks enforced", + f"`{install_summary.get('installer_permission_failure_count')}` permission failures", + f"`{benchmark_summary.get('required_artifact_count')}` required artifacts", + f"`{benchmark_summary.get('command_count')}` reproduction commands", + ( + f"initial load `{context_stats.get('estimated_initial_load_tokens')}/" + f"{context_stats.get('context_budget_limit')}`" + ), + f"target count is `{ci_target_count}`", + ] + missing_review_snippets = [snippet for snippet in expected_review_snippets if snippet not in skill_os2_review] + return { + "key": "skill-os-2-review-current-evidence", + "label": "Skill OS 2.0 review summary mirrors current evidence", + "status": "pass" if not missing_review_snippets else "fail", + "expected": expected_review_snippets, + "actual": "all present" if not missing_review_snippets else {"missing": missing_review_snippets}, + "paths": [ + required_text_reports["skill_os2_review"], + required_reports["review_studio"], + required_reports["package_verification"], + required_reports["install_simulation"], + required_reports["trust"], + required_reports["context_budget"], + required_reports["benchmark"], + "scripts/ci_test.py", + ], + "detail": ( + "Manual 2.0 review summaries must not drift from generated gate, package, trust, " + "context, benchmark, or CI evidence." + ), + } diff --git a/scripts/evidence_consistency_world_class.py b/scripts/evidence_consistency_world_class.py new file mode 100644 index 0000000..10a6f96 --- /dev/null +++ b/scripts/evidence_consistency_world_class.py @@ -0,0 +1,222 @@ +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_evidence_consistency.py to isolate world-class evidence workflow consistency checks." + + +def keyed_items(payload: dict[str, Any], collection_key: str) -> dict[str, dict[str, Any]]: + collection = payload.get(collection_key) + if not isinstance(collection, list): + return {} + keyed: dict[str, dict[str, Any]] = {} + for item in collection: + if not isinstance(item, dict): + continue + key = item.get("key") or item.get("evidence_key") + if isinstance(key, str): + keyed[key] = item + return keyed + + +def command_key_set(item: dict[str, Any]) -> set[str]: + commands = item.get("commands") + if isinstance(commands, dict): + return {key for key, value in commands.items() if isinstance(key, str) and value} + if isinstance(commands, list): + keys: set[str] = set() + for command in commands: + if isinstance(command, dict) and isinstance(command.get("key"), str) and command.get("command"): + keys.add(command["key"]) + return keys + return set() + + +def world_class_review_action_steps(review_studio: dict[str, Any]) -> dict[str, dict[str, Any]]: + actions = review_studio.get("review_actions") + if not isinstance(actions, list): + return {} + for action in actions: + if not isinstance(action, dict) or action.get("gate_key") != "world-class-evidence": + continue + steps = action.get("evidence_steps") + if not isinstance(steps, list): + return {} + keyed: dict[str, dict[str, Any]] = {} + for step in steps: + if isinstance(step, dict) and isinstance(step.get("key"), str): + keyed[step["key"]] = step + return keyed + return {} + + +def command_groups_present(command_keys: set[str]) -> dict[str, bool]: + return { + "prepare_submission": "prepare_submission" in command_keys, + "validate_intake": "validate_intake" in command_keys, + "submission_review": bool({"submission_review", "review_queue"} & command_keys), + "refresh_ledger": "refresh_ledger" in command_keys, + "guard_claim": "guard_claim" in command_keys, + } + + +def has_next_action(item: dict[str, Any]) -> bool: + for key in ["next_action", "audit_next_action"]: + value = item.get(key) + if isinstance(value, str) and value.strip(): + return True + return False + + +def command_expectations(keys: list[str]) -> dict[str, dict[str, dict[str, bool]]]: + expected_group = { + "prepare_submission": True, + "validate_intake": True, + "submission_review": True, + "refresh_ledger": True, + "guard_claim": True, + } + return { + key: { + "intake": dict(expected_group), + "operator_runbook": dict(expected_group), + "review_studio": dict(expected_group), + } + for key in keys + } + + +def build_world_class_workflow_check( + *, + ledger: dict[str, Any], + world_class_plan: dict[str, Any], + world_class_intake: dict[str, Any], + world_class_submission_review: dict[str, Any], + world_class_operator_runbook: dict[str, Any], + review_studio: dict[str, Any], + report_paths: dict[str, str], +) -> dict[str, Any]: + ledger_summary = ledger.get("summary") if isinstance(ledger.get("summary"), dict) else {} + plan_summary = world_class_plan.get("summary") if isinstance(world_class_plan.get("summary"), dict) else {} + intake_summary = world_class_intake.get("summary") if isinstance(world_class_intake.get("summary"), dict) else {} + submission_review_summary = ( + world_class_submission_review.get("summary") + if isinstance(world_class_submission_review.get("summary"), dict) + else {} + ) + operator_runbook_summary = ( + world_class_operator_runbook.get("summary") + if isinstance(world_class_operator_runbook.get("summary"), dict) + else {} + ) + + ledger_items = keyed_items(ledger, "entries") + plan_tasks = keyed_items(world_class_plan, "tasks") + intake_checklist = keyed_items(world_class_intake, "operator_checklist") + submission_review_items = keyed_items(world_class_submission_review, "items") + operator_runbook_items = keyed_items(world_class_operator_runbook, "items") + coordination_plan = ( + world_class_operator_runbook.get("coordination_plan") + if isinstance(world_class_operator_runbook.get("coordination_plan"), list) + else [] + ) + release_gate = ( + world_class_operator_runbook.get("release_gate") + if isinstance(world_class_operator_runbook.get("release_gate"), dict) + else {} + ) + review_action_steps = world_class_review_action_steps(review_studio) + pending_keys = sorted(key for key, item in ledger_items.items() if item.get("status") != "accepted") + operator_coordination_keys = sorted( + key + for key in {str(step.get("evidence_key", "")) for step in coordination_plan if isinstance(step, dict)} + if key + ) + + actual_command_groups = { + key: { + "intake": command_groups_present(command_key_set(intake_checklist.get(key, {}))), + "operator_runbook": command_groups_present(command_key_set(operator_runbook_items.get(key, {}))), + "review_studio": command_groups_present(command_key_set(review_action_steps.get(key, {}))), + } + for key in pending_keys + } + expected = { + "keys": pending_keys, + "pending_count": ledger_summary.get("pending_count"), + "human_pending_count": ledger_summary.get("human_pending_count"), + "external_pending_count": ledger_summary.get("external_pending_count"), + "source_check_count": ledger_summary.get("source_check_count"), + "source_pass_count": ledger_summary.get("source_pass_count"), + "source_blocked_count": ledger_summary.get("source_blocked_count"), + "plan_keys": pending_keys, + "intake_keys": pending_keys, + "submission_review_keys": pending_keys, + "operator_runbook_keys": pending_keys, + "operator_coordination_keys": pending_keys, + "operator_coordination_counts_as_completion": False, + "operator_release_gate_ready": ledger_summary.get("ready_to_claim_world_class") is True, + "operator_release_gate_counts_as_completion": False, + "review_studio_keys": pending_keys, + "intake_ready_to_claim_world_class": False, + "submission_review_ready_to_claim_world_class": False, + "submission_review_counts_as_completion": False, + "operator_runbook_ready_to_claim_world_class": False, + "operator_runbook_counts_as_completion": False, + "next_actions_present": {key: True for key in pending_keys}, + "commands": command_expectations(pending_keys), + } + actual = { + "keys": pending_keys, + "pending_count": plan_summary.get("task_count"), + "human_pending_count": plan_summary.get("human_task_count"), + "external_pending_count": plan_summary.get("external_task_count"), + "source_check_count": submission_review_summary.get("source_check_count"), + "source_pass_count": submission_review_summary.get("source_pass_count"), + "source_blocked_count": submission_review_summary.get("source_blocked_count"), + "plan_keys": sorted(plan_tasks), + "intake_keys": sorted(intake_checklist), + "submission_review_keys": sorted(submission_review_items), + "operator_runbook_keys": sorted(operator_runbook_items), + "operator_coordination_keys": operator_coordination_keys, + "operator_coordination_counts_as_completion": operator_runbook_summary.get( + "coordination_counts_as_completion" + ), + "operator_release_gate_ready": release_gate.get("ready"), + "operator_release_gate_counts_as_completion": release_gate.get("counts_as_completion"), + "review_studio_keys": sorted(review_action_steps), + "intake_ready_to_claim_world_class": intake_summary.get("ready_to_claim_world_class"), + "submission_review_ready_to_claim_world_class": submission_review_summary.get("ready_to_claim_world_class"), + "submission_review_counts_as_completion": submission_review_summary.get( + "review_counts_submission_as_completion" + ), + "operator_runbook_ready_to_claim_world_class": operator_runbook_summary.get("ready_to_claim_world_class"), + "operator_runbook_counts_as_completion": operator_runbook_summary.get("runbook_counts_as_completion"), + "next_actions_present": { + key: all( + has_next_action(collection.get(key, {})) + for collection in [plan_tasks, intake_checklist, submission_review_items, review_action_steps] + ) + for key in pending_keys + }, + "commands": actual_command_groups, + } + return { + "key": "world-class-evidence-workflow-coverage", + "label": "World-class evidence workflows cover every pending ledger entry", + "status": "pass" if expected == actual else "fail", + "expected": expected, + "actual": actual, + "paths": [ + report_paths["world_class_ledger"], + report_paths["world_class_plan"], + report_paths["world_class_intake"], + report_paths["world_class_submission_review"], + report_paths["world_class_operator_runbook"], + report_paths["review_studio"], + ], + "detail": ( + "Every pending world-class evidence key must have matching plan, intake, submission review, " + "operator runbook, and Review Studio actions without counting planned work as completion." + ), + } diff --git a/scripts/export_skill_ir.py b/scripts/export_skill_ir.py new file mode 100644 index 0000000..f02b913 --- /dev/null +++ b/scripts/export_skill_ir.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from datetime import date +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +ROOT = Path(__file__).resolve().parent.parent +SCHEMA_VERSION = "2.0.0" +IR_SCHEMA = ROOT / "skill-ir" / "schema.json" +KEY_REPORTS = [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json", +] + + +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(text: str) -> tuple[dict[str, Any], str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data: dict[str, Any] = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def parse_sections(body: str) -> dict[str, str]: + sections: dict[str, list[str]] = {"_preamble": []} + current = "_preamble" + for line in body.splitlines(): + if line.startswith("## "): + current = line[3:].strip() + sections[current] = [] + continue + sections.setdefault(current, []).append(line) + return {name: "\n".join(lines).strip() for name, lines in sections.items()} + + +def extract_list_items(text: str, limit: int = 12) -> list[str]: + items = [] + for line in text.splitlines(): + stripped = line.strip() + match = re.match(r"^(?:[-*]|\d+\.)\s+(.*)$", stripped) + if match: + item = match.group(1).strip() + if item: + items.append(item) + if len(items) >= limit: + break + return items + + +def compact_unique(items: list[str], limit: int = 12) -> list[str]: + seen = set() + output = [] + for item in items: + value = str(item).strip() + key = value.lower() + if not value or key in seen: + continue + seen.add(key) + output.append(value) + if len(output) >= limit: + break + return output + + +def trigger_samples(skill_dir: Path, key: str, limit: int = 8) -> list[str]: + payload = load_json(skill_dir / "evals" / "trigger_cases.json") + values = payload.get(key, []) + samples = [] + if isinstance(values, list): + for item in values: + if isinstance(item, dict): + samples.append(str(item.get("text", "")).strip()) + else: + samples.append(str(item).strip()) + return compact_unique(samples, limit=limit) + + +def report_list(skill_dir: Path) -> list[str]: + return [rel for rel in KEY_REPORTS if (skill_dir / rel).exists()] + + +def file_list(skill_dir: Path, folder: str, suffixes: set[str] | None = None, limit: int | None = None) -> list[str]: + target = skill_dir / folder + if not target.exists(): + return [] + paths = [] + for path in sorted(target.rglob("*")): + if not path.is_file(): + continue + if suffixes is not None and path.suffix not in suffixes: + continue + paths.append(str(path.relative_to(skill_dir))) + if limit is not None and len(paths) >= limit: + break + return paths + + +def workflow_steps(sections: dict[str, str]) -> list[str]: + for key in ("Compact Workflow", "Workflow", "How It Works", "Runbook", "Quick Start"): + items = extract_list_items(sections.get(key, ""), limit=10) + if items: + return items + return ["Understand the request.", "Execute the main task.", "Validate the result."] + + +def decision_points(sections: dict[str, str], system_model: dict[str, Any]) -> list[str]: + points = [] + for key in ("Router Rules", "Modes", "Output Contract"): + points.extend(extract_list_items(sections.get(key, ""), limit=6)) + boundary = system_model.get("boundary_map", {}) if isinstance(system_model, dict) else {} + points.extend(boundary.get("human_judgment_boundary", []) if isinstance(boundary, dict) else []) + return compact_unique(points, limit=10) + + +def failure_modes(skill_dir: Path, output_risk: dict[str, Any], system_model: dict[str, Any]) -> list[str]: + modes = [] + modes.extend(output_risk.get("top_risks", [])[:6]) + drift = system_model.get("drift_watch", []) if isinstance(system_model, dict) else [] + for item in drift: + if isinstance(item, dict): + modes.append(str(item.get("watch_signal", "") or item.get("risk", ""))) + failure_cases = skill_dir / "evals" / "failure-cases.md" + if failure_cases.exists(): + modes.extend(extract_list_items(failure_cases.read_text(encoding="utf-8"), limit=4)) + return compact_unique(modes, limit=10) + + +def risk_level_from_count(count: int) -> str: + if count >= 5: + return "high" + if count >= 2: + return "medium" + return "low" + + +def build_skill_ir(skill_dir: Path) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + sections = parse_sections(body) + manifest = load_json(skill_dir / "manifest.json") + interface_data = load_yaml(skill_dir / "agents" / "interface.yaml") + intent_context = load_json(skill_dir / "reports" / "intent-context.json") + output_risk = load_json(skill_dir / "reports" / "output-risk-profile.json") + system_model = load_json(skill_dir / "reports" / "system-model.json") + + name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name) + description = str(frontmatter.get("description") or intent_context.get("description") or "") + title = str(interface_data.get("interface", {}).get("display_name") or name.replace("-", " ").title()) + job = str(intent_context.get("job") or system_model.get("boundary_map", {}).get("owned_job") or description) + exclusions = intent_context.get("exclusions", []) + if not exclusions: + exclusions = system_model.get("boundary_map", {}).get("non_goals", []) + + scripts = file_list(skill_dir, "scripts", suffixes={".py", ".sh", ".js", ".ts"}) + references = file_list(skill_dir, "references", suffixes={".md", ".txt", ".json", ".yaml", ".yml"}) + assets = file_list(skill_dir, "assets") + file_list(skill_dir, "templates") + trigger_eval_files = file_list(skill_dir, "evals", suffixes={".json", ".jsonl", ".md"}) + output_eval_files = file_list(skill_dir, "evals/output", suffixes={".json", ".jsonl", ".md"}) + + target_platforms = manifest.get("target_platforms") or interface_data.get("compatibility", {}).get("adapter_targets", []) + maturity = str(manifest.get("maturity_tier") or manifest.get("skill_archetype") or "scaffold") + trust_boundary = "external" if maturity == "governed" else "team" if maturity in {"production", "library"} else "personal" + output_risk_count = len(output_risk.get("risk_families", [])) + execution_risk = "medium" if scripts else "low" + + return { + "schema_version": SCHEMA_VERSION, + "name": name, + "title": title, + "job_to_be_done": job, + "trigger_surface": { + "description": description, + "should_trigger": trigger_samples(skill_dir, "should_trigger") or [description], + "should_not_trigger": compact_unique([*trigger_samples(skill_dir, "should_not_trigger"), *map(str, exclusions)], 10), + "edge_cases": trigger_samples(skill_dir, "near_neighbor"), + }, + "workflow": { + "steps": workflow_steps(sections), + "decision_points": decision_points(sections, system_model), + "failure_modes": failure_modes(skill_dir, output_risk, system_model), + }, + "resources": { + "references": references, + "scripts": scripts, + "assets": assets, + "reports": report_list(skill_dir), + }, + "eval_plan": { + "trigger": trigger_eval_files, + "output": output_eval_files, + "adversarial": file_list(skill_dir, "evals/adversarial", suffixes={".json", ".jsonl", ".md"}), + "baseline": "without_skill", + }, + "risk": { + "output_risk": risk_level_from_count(output_risk_count), + "execution_risk": execution_risk, + "trust_boundary": trust_boundary, + }, + "governance": { + "owner": str(manifest.get("owner", "")), + "maturity": maturity if maturity in {"scaffold", "production", "library", "governed"} else "scaffold", + "review_cadence": str(manifest.get("review_cadence", "")), + "review_due": str(manifest.get("review_due", "")), + }, + "targets": [str(item) for item in target_platforms], + "source_files": compact_unique( + [ + "SKILL.md", + "manifest.json" if (skill_dir / "manifest.json").exists() else "", + "agents/interface.yaml" if (skill_dir / "agents" / "interface.yaml").exists() else "", + "reports/intent-context.json" if (skill_dir / "reports" / "intent-context.json").exists() else "", + "reports/output-risk-profile.json" if (skill_dir / "reports" / "output-risk-profile.json").exists() else "", + "reports/system-model.json" if (skill_dir / "reports" / "system-model.json").exists() else "", + ], + limit=20, + ), + } + + +def validate_ir(payload: dict[str, Any]) -> list[str]: + failures = [] + for key in ("schema_version", "name", "job_to_be_done", "trigger_surface", "workflow", "resources", "eval_plan", "risk", "governance"): + if key not in payload: + failures.append(f"Missing required field: {key}") + if payload.get("schema_version") != SCHEMA_VERSION: + failures.append(f"schema_version must be {SCHEMA_VERSION}") + if not payload.get("trigger_surface", {}).get("description"): + failures.append("trigger_surface.description is required") + if not payload.get("trigger_surface", {}).get("should_trigger"): + failures.append("trigger_surface.should_trigger needs at least one sample") + if not payload.get("workflow", {}).get("steps"): + failures.append("workflow.steps needs at least one step") + if payload.get("risk", {}).get("output_risk") not in {"low", "medium", "high"}: + failures.append("risk.output_risk must be low, medium, or high") + if payload.get("governance", {}).get("maturity") not in {"scaffold", "production", "library", "governed"}: + failures.append("governance.maturity is invalid") + return failures + + +def default_output_path(skill_dir: Path, payload: dict[str, Any]) -> Path: + if skill_dir.resolve() == ROOT.resolve(): + return ROOT / "skill-ir" / "examples" / f"{payload['name']}.json" + return skill_dir / "reports" / "skill-ir.json" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Export a platform-neutral Skill IR document from a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json") + parser.add_argument("--validate-only", action="store_true") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + payload = build_skill_ir(skill_dir) + failures = validate_ir(payload) + output_json = Path(args.output_json).resolve() if args.output_json else default_output_path(skill_dir, payload) + if not args.validate_only: + output_json.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + result = { + "ok": not failures, + "schema": str(IR_SCHEMA), + "skill_dir": str(skill_dir), + "artifacts": {"json": str(output_json)}, + "summary": { + "name": payload.get("name"), + "maturity": payload.get("governance", {}).get("maturity"), + "target_count": len(payload.get("targets", [])), + "trigger_samples": len(payload.get("trigger_surface", {}).get("should_trigger", [])), + "resource_count": sum(len(payload.get("resources", {}).get(key, [])) for key in ("references", "scripts", "assets", "reports")), + }, + "failures": failures, + } + print(json.dumps(result, ensure_ascii=False, indent=2)) + raise SystemExit(0 if result["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/github_benchmark_scan.py b/scripts/github_benchmark_scan.py new file mode 100644 index 0000000..7006671 --- /dev/null +++ b/scripts/github_benchmark_scan.py @@ -0,0 +1,431 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import re +from pathlib import Path +from typing import Any +from urllib.parse import quote, urlparse +from urllib.request import Request, urlopen + + +API_ROOT = "https://api.github.com" +API_VERSION = "2022-11-28" +USER_AGENT = "yao-meta-skill/1.0" +DEFAULT_TOP_N = 3 +DEFAULT_PER_PAGE = 8 +MIN_STARS = 50 +ALLOWED_API_HOSTS = {"api.github.com"} + +STOPWORDS = { + "the", + "and", + "for", + "with", + "into", + "from", + "this", + "that", + "your", + "will", + "make", + "turn", + "build", + "create", + "reusable", + "skill", + "package", + "agent", + "workflow", + "prompt", + "notes", + "docs", + "internal", + "team", + "real", + "output", + "outputs", + "input", + "inputs", +} + +PATTERN_RULES = [ + { + "category": "method", + "keywords": ["workflow", "process", "pipeline", "orchestrat", "runbook", "playbook"], + "borrow": "Borrow the way it turns a messy workflow into a repeatable operating path.", + "avoid": "Do not import process overhead that only exists for that project's scale.", + }, + { + "category": "execution", + "keywords": ["cli", "command", "shell", "script", "automation", "toolchain"], + "borrow": "Borrow the clear execution entrypoints and command structure.", + "avoid": "Do not copy repo-specific commands or environment assumptions verbatim.", + }, + { + "category": "evaluation", + "keywords": ["test", "benchmark", "eval", "assert", "validation", "quality"], + "borrow": "Borrow explicit validation and quality gates that make iteration safer.", + "avoid": "Do not clone heavyweight evaluation scaffolding if a lighter gate is enough here.", + }, + { + "category": "structure", + "keywords": ["template", "example", "guide", "docs", "reference", "starter"], + "borrow": "Borrow the way it separates explanation, examples, and reusable structure.", + "avoid": "Do not mirror documentation bulk that adds context cost without improving reliability.", + }, + { + "category": "portability", + "keywords": ["adapter", "portable", "plugin", "integration", "export", "compatibility"], + "borrow": "Borrow how it keeps core semantics stable across environments or integrations.", + "avoid": "Do not inherit platform lock-in or vendor-specific branching unless truly required.", + }, + { + "category": "governance", + "keywords": ["govern", "policy", "security", "audit", "compliance", "review"], + "borrow": "Borrow the explicit review, safety, or operational trust boundaries.", + "avoid": "Do not import compliance-heavy process where the new skill does not need it.", + }, +] + + +def github_headers() -> dict[str, str]: + headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": API_VERSION, + "User-Agent": USER_AGENT, + } + token = os.getenv("GITHUB_TOKEN") or os.getenv("GH_TOKEN") + if token: + headers["Authorization"] = f"Bearer {token}" + return headers + + +def ensure_allowed_api_url(url: str) -> str: + parsed = urlparse(url) + if parsed.scheme != "https" or parsed.hostname not in ALLOWED_API_HOSTS: + raise ValueError(f"Refusing outbound request to non-GitHub API host: {url}") + return url + + +def api_get(url: str) -> dict[str, Any]: + request = Request(ensure_allowed_api_url(url), headers=github_headers()) + with urlopen(request, timeout=15) as response: # nosec B310 - GitHub API fetch + data = response.read().decode("utf-8") + return json.loads(data) + + +def load_fixture_bundle(fixture_dir: Path | None) -> dict[str, Any] | None: + if not fixture_dir: + return None + bundle_path = fixture_dir / "bundle.json" + if not bundle_path.exists(): + raise FileNotFoundError(f"Missing fixture bundle: {bundle_path}") + return json.loads(bundle_path.read_text(encoding="utf-8")) + + +def build_query(seed_text: str) -> str: + words = re.findall(r"[A-Za-z][A-Za-z0-9_-]{2,}", seed_text.lower()) + keywords = [] + seen = set() + for word in words: + if word in STOPWORDS or word in seen: + continue + seen.add(word) + keywords.append(word) + if len(keywords) == 4: + break + if keywords: + return " ".join(keywords) + compact = re.sub(r"\s+", " ", seed_text.strip()) + return compact[:64] or "agent workflow automation" + + +def search_repositories(query: str, top_n: int, fixture_bundle: dict[str, Any] | None = None) -> list[dict[str, Any]]: + if fixture_bundle is not None: + items = fixture_bundle.get("search_items", []) + return items[:top_n] + + github_query = f"{query} fork:false archived:false stars:>={MIN_STARS}" + url = ( + f"{API_ROOT}/search/repositories?q={quote(github_query)}" + f"&sort=stars&order=desc&per_page={DEFAULT_PER_PAGE}" + ) + payload = api_get(url) + items = payload.get("items", []) + filtered = [item for item in items if not item.get("fork") and not item.get("archived")] + return filtered[:top_n] + + +def fetch_readme(full_name: str, fixture_bundle: dict[str, Any] | None = None) -> str: + if fixture_bundle is not None: + return fixture_bundle.get("readmes", {}).get(full_name, "") + + url = f"{API_ROOT}/repos/{full_name}/readme" + request = Request( + ensure_allowed_api_url(url), + headers={**github_headers(), "Accept": "application/vnd.github.raw+json"}, + ) + with urlopen(request, timeout=15) as response: # nosec B310 - GitHub API fetch + return response.read().decode("utf-8", errors="replace") + + +def detect_patterns(text: str) -> list[dict[str, str]]: + lowered = text.lower() + hits = [] + for rule in PATTERN_RULES: + matched = [token for token in rule["keywords"] if token in lowered] + if matched: + hits.append( + { + "category": rule["category"], + "matched": ", ".join(matched[:4]), + "borrow": rule["borrow"], + "avoid": rule["avoid"], + } + ) + return hits[:4] + + +def repo_summary(repo: dict[str, Any], readme_text: str) -> dict[str, Any]: + combined = " ".join( + filter( + None, + [ + repo.get("name", ""), + repo.get("description", "") or "", + " ".join(repo.get("topics") or []), + readme_text[:4000], + ], + ) + ) + patterns = detect_patterns(combined) + if not patterns: + patterns = [ + { + "category": "general", + "matched": "high-level fit", + "borrow": "Borrow only the parts that make the new skill clearer, safer, or easier to reuse.", + "avoid": "Do not import source-specific scope, branding, or excess weight.", + } + ] + + borrow = [item["borrow"] for item in patterns[:3]] + avoid = [item["avoid"] for item in patterns[:3]] + return { + "full_name": repo.get("full_name"), + "html_url": repo.get("html_url"), + "description": repo.get("description"), + "stars": repo.get("stargazers_count", 0), + "topics": repo.get("topics") or [], + "primary_language": repo.get("language"), + "patterns": patterns, + "borrow": borrow, + "avoid": avoid, + "readme_excerpt": readme_text[:1200].strip(), + } + + +def cross_repo_insights(repos: list[dict[str, Any]]) -> dict[str, list[str]]: + borrow = [] + avoid = [] + seen_borrow = set() + seen_avoid = set() + if not repos: + return { + "borrow": [], + "avoid": [], + } + for repo in repos: + for item in repo.get("borrow", []): + if item not in seen_borrow: + seen_borrow.add(item) + borrow.append(item) + for item in repo.get("avoid", []): + if item not in seen_avoid: + seen_avoid.add(item) + avoid.append(item) + borrow.append("Ask the user which of these patterns feels most worth borrowing before freezing the package shape.") + return {"borrow": borrow[:6], "avoid": avoid[:6]} + + +def repo_to_external_reference(repo: dict[str, Any]) -> dict[str, str]: + primary = repo.get("patterns", [{}])[0] + return { + "name": repo.get("full_name") or "Unnamed repo", + "category": primary.get("category", "general"), + "borrow": repo.get("borrow", ["Borrow the strongest reusable pattern."])[0], + "avoid": repo.get("avoid", ["Do not copy source-specific language or excess scope."])[0], + "source": "external", + } + + +def render_markdown(payload: dict[str, Any]) -> str: + repo_count = len(payload["repositories"]) + next_step = ( + f"Review the {repo_count} benchmark object{'s' if repo_count != 1 else ''} below, " + "then decide whether any of their patterns are worth borrowing into the new skill." + if repo_count + else "No benchmark repositories were collected; adjust the query or attach manual references before treating the scan as evidence." + ) + lines = [ + "# GitHub Benchmark Scan", + "", + f"- Query: `{payload['query']}`", + f"- Source: `{payload['source']}`", + f"- Top repositories: `{repo_count}`", + "", + "## Suggested Next Step", + "", + next_step, + "", + ] + + if not payload["repositories"]: + lines.extend( + [ + "No benchmark repositories were collected.", + "", + "Possible reasons:", + "- the query was too weak or too local", + "- the network or API rate limit blocked the scan", + "- this skill idea may need a manual benchmark query", + ] + ) + return "\n".join(lines).strip() + "\n" + + lines.extend([f"## Top {repo_count} Benchmark Repositories", ""]) + for repo in payload["repositories"]: + lines.extend( + [ + f"### {repo['full_name']}", + f"- URL: {repo['html_url']}", + f"- Stars: `{repo['stars']}`", + f"- Description: {repo.get('description') or 'No public description provided.'}", + f"- Topics: {', '.join(repo.get('topics') or []) or 'None'}", + "", + "#### Patterns worth studying", + "", + ] + ) + for pattern in repo["patterns"]: + lines.append( + f"- **{pattern['category']}** (`{pattern['matched']}`): {pattern['borrow']}" + ) + lines.extend(["", "#### Borrow", ""]) + for item in repo["borrow"]: + lines.append(f"- {item}") + lines.extend(["", "#### Avoid", ""]) + for item in repo["avoid"]: + lines.append(f"- {item}") + lines.extend(["", "#### README glimpse", "", repo["readme_excerpt"] or "No README excerpt collected.", ""]) + + lines.extend(["## Cross-Repo Borrow Recommendations", ""]) + for item in payload["cross_repo"]["borrow"]: + lines.append(f"- {item}") + + lines.extend(["", "## Cross-Repo Avoid Recommendations", ""]) + for item in payload["cross_repo"]["avoid"]: + lines.append(f"- {item}") + + lines.extend( + [ + "", + "## Borrow Prompt", + "", + payload["borrow_prompt"], + "", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def run_github_benchmark_scan( + skill_dir: Path, + query: str, + top_n: int = DEFAULT_TOP_N, + fixture_dir: Path | None = None, + output_md: 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_md = output_md or reports_dir / "github-benchmark-scan.md" + output_json = output_json or reports_dir / "github-benchmark-scan.json" + fixture_bundle = load_fixture_bundle(fixture_dir) + + warnings = [] + try: + repos = search_repositories(query, top_n=top_n, fixture_bundle=fixture_bundle) + repo_summaries = [] + for repo in repos: + readme_text = fetch_readme(repo["full_name"], fixture_bundle=fixture_bundle) + repo_summaries.append(repo_summary(repo, readme_text)) + cross_repo = cross_repo_insights(repo_summaries) + payload = { + "ok": True, + "query": query, + "source": "fixture" if fixture_bundle is not None else "github-api", + "network_boundary": "github-api-only", + "repositories": repo_summaries, + "cross_repo": cross_repo, + "external_references": [repo_to_external_reference(repo) for repo in repo_summaries], + "borrow_prompt": ( + f"I found {len(repo_summaries)} public GitHub project" + f"{'s' if len(repo_summaries) != 1 else ''} worth studying. " + "Do you want to borrow any of these patterns for method, structure, execution, or portability?" + if repo_summaries + else "No benchmark suggestions were collected yet. You can retry with a stronger query or add manual references." + ), + "warnings": warnings, + } + except Exception as exc: # pragma: no cover - network failures are environment-dependent + payload = { + "ok": False, + "query": query, + "source": "fixture" if fixture_bundle is not None else "github-api", + "network_boundary": "github-api-only", + "repositories": [], + "cross_repo": { + "borrow": [], + "avoid": [], + }, + "external_references": [], + "borrow_prompt": "No benchmark suggestions were collected yet. You can retry with a stronger query or add manual references.", + "warnings": [f"GitHub benchmark scan failed: {exc}"], + } + + output_md.write_text(render_markdown(payload), encoding="utf-8") + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + payload["artifacts"] = { + "markdown": str(output_md), + "json": str(output_json), + } + payload["skill_dir"] = str(skill_dir) + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Search GitHub for top benchmark repositories and extract reusable patterns.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--query", required=True) + parser.add_argument("--top-n", type=int, default=DEFAULT_TOP_N) + parser.add_argument("--fixture-dir") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + + result = run_github_benchmark_scan( + Path(args.skill_dir), + query=args.query, + top_n=args.top_n, + fixture_dir=Path(args.fixture_dir).resolve() if args.fixture_dir else None, + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/governance_check.py b/scripts/governance_check.py new file mode 100644 index 0000000..3654faa --- /dev/null +++ b/scripts/governance_check.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import datetime +from pathlib import Path + + +ALLOWED_STATUS = {"experimental", "active", "deprecated"} +ALLOWED_MATURITY = {"scaffold", "production", "library", "governed"} +ALLOWED_REVIEW_CADENCE = {"monthly", "quarterly", "semiannual", "annual", "per-release"} +DECLARED_MATURITY_MIN_SCORE = { + "scaffold": 0, + "production": 80, + "library": 85, + "governed": 90, +} + + +def load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def read_frontmatter(skill_md: Path) -> dict: + if not skill_md.exists(): + return {} + text = skill_md.read_text(encoding="utf-8") + if not text.startswith("---"): + return {} + parts = text.split("---", 2) + if len(parts) < 3: + return {} + payload = {} + for line in parts[1].splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + payload[key.strip()] = value.strip().strip("'\"") + return payload + + +def has_files(path: Path) -> bool: + return path.exists() and any(child.is_file() for child in path.rglob("*")) + + +def score_label(score: int) -> str: + if score >= 90: + return "governed" + if score >= 80: + return "production" + if score >= 65: + return "reusable" + if score >= 45: + return "emerging" + return "draft" + + +def compute_score(root: Path, manifest: dict, frontmatter: dict, skill_text: str, manifest_valid: bool) -> tuple[int, dict]: + breakdown = { + "metadata_integrity": 0, + "ownership_and_review": 0, + "boundary_and_eval": 0, + "operational_assets": 0, + "maintenance_evidence": 0, + } + + if manifest_valid and manifest: + breakdown["metadata_integrity"] += 4 + if manifest.get("name") and frontmatter.get("name") and manifest["name"] == frontmatter["name"]: + breakdown["metadata_integrity"] += 4 + if manifest.get("version"): + breakdown["metadata_integrity"] += 3 + if manifest.get("updated_at"): + breakdown["metadata_integrity"] += 3 + if manifest.get("status") in ALLOWED_STATUS: + breakdown["metadata_integrity"] += 3 + if manifest.get("maturity_tier") in ALLOWED_MATURITY: + breakdown["metadata_integrity"] += 3 + + if manifest.get("owner"): + breakdown["ownership_and_review"] += 10 + if manifest.get("review_cadence") in ALLOWED_REVIEW_CADENCE: + breakdown["ownership_and_review"] += 10 + + if frontmatter.get("description"): + breakdown["boundary_and_eval"] += 5 + if any(marker in skill_text.lower() for marker in ("do not use", "out of scope", "should not trigger", "do not")): + breakdown["boundary_and_eval"] += 10 + if has_files(root / "evals"): + breakdown["boundary_and_eval"] += 10 + + if (root / "agents" / "interface.yaml").exists(): + breakdown["operational_assets"] += 5 + if has_files(root / "references"): + breakdown["operational_assets"] += 5 + if has_files(root / "scripts"): + breakdown["operational_assets"] += 5 + if manifest.get("factory_components") or manifest.get("target_platforms"): + breakdown["operational_assets"] += 5 + + if has_files(root / "reports"): + breakdown["maintenance_evidence"] += 5 + if has_files(root / "evals" / "history") or has_files(root / "failures"): + breakdown["maintenance_evidence"] += 5 + if ( + has_files(root / "outputs") + or has_files(root / "assets") + or has_files(root / "examples") + or has_files(root / "tests") + ): + breakdown["maintenance_evidence"] += 5 + + total = sum(breakdown.values()) + return total, breakdown + + +def main() -> None: + parser = argparse.ArgumentParser(description="Check skill governance metadata and lifecycle readiness.") + parser.add_argument("skill_dir") + parser.add_argument("--require-manifest", action="store_true") + args = parser.parse_args() + + root = Path(args.skill_dir).resolve() + manifest_path = root / "manifest.json" + skill_md = root / "SKILL.md" + failures = [] + warnings = [] + details = {"skill_dir": str(root), "manifest_present": manifest_path.exists()} + + frontmatter = read_frontmatter(skill_md) + skill_text = skill_md.read_text(encoding="utf-8") if skill_md.exists() else "" + manifest = {} + manifest_valid = False + if manifest_path.exists(): + try: + manifest = load_json(manifest_path) + manifest_valid = True + except json.JSONDecodeError as exc: + failures.append(f"Invalid manifest.json: {exc}") + elif args.require_manifest: + failures.append("Missing manifest.json") + else: + warnings.append("No manifest.json; governance metadata is unavailable.") + + if manifest: + required = ["name", "version", "owner", "updated_at", "review_cadence", "status", "maturity_tier", "lifecycle_stage"] + missing = [field for field in required if not manifest.get(field)] + if missing: + failures.append(f"Missing manifest fields: {', '.join(missing)}") + + if manifest.get("status") and manifest["status"] not in ALLOWED_STATUS: + failures.append(f"Invalid status: {manifest['status']}") + if manifest.get("maturity_tier") and manifest["maturity_tier"] not in ALLOWED_MATURITY: + failures.append(f"Invalid maturity_tier: {manifest['maturity_tier']}") + if manifest.get("lifecycle_stage") and manifest["lifecycle_stage"] not in ALLOWED_MATURITY: + failures.append(f"Invalid lifecycle_stage: {manifest['lifecycle_stage']}") + if manifest.get("review_cadence") and manifest["review_cadence"] not in ALLOWED_REVIEW_CADENCE: + failures.append(f"Invalid review_cadence: {manifest['review_cadence']}") + if manifest.get("updated_at"): + try: + datetime.strptime(manifest["updated_at"], "%Y-%m-%d") + except ValueError: + failures.append("updated_at must use YYYY-MM-DD") + + if frontmatter.get("name") and manifest.get("name") and frontmatter["name"] != manifest["name"]: + failures.append("manifest name does not match SKILL.md frontmatter name") + + if manifest.get("status") == "deprecated" and not manifest.get("deprecation_note"): + warnings.append("Deprecated skill should include deprecation_note in manifest.json.") + + score, breakdown = compute_score(root, manifest, frontmatter, skill_text, manifest_valid) + computed_label = score_label(score) + declared_tier = manifest.get("maturity_tier") + required_minimum = DECLARED_MATURITY_MIN_SCORE.get(declared_tier) + if declared_tier in ALLOWED_MATURITY and required_minimum is not None and score < required_minimum: + warnings.append( + f"Maturity tier says {declared_tier} but governance score is {score}/100, below the recommended minimum {required_minimum}." + ) + + report = { + "ok": not failures, + "failures": failures, + "warnings": warnings, + "details": { + **details, + "frontmatter_name": frontmatter.get("name"), + "manifest_name": manifest.get("name"), + "status": manifest.get("status"), + "maturity_tier": manifest.get("maturity_tier"), + "review_cadence": manifest.get("review_cadence"), + "governance_score": score, + "declared_maturity_minimum": required_minimum, + "computed_governance_band": computed_label, + "computed_maturity_tier": computed_label, + "score_breakdown": breakdown, + }, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + if failures: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/html_rendering.py b/scripts/html_rendering.py new file mode 100644 index 0000000..afb7f75 --- /dev/null +++ b/scripts/html_rendering.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +"""Shared HTML rendering helpers for static report generators.""" + +import html +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Used by report renderers to escape HTML while preserving meaningful falsey values." + + +def html_text(value: Any) -> str: + """Escape text for HTML without dropping 0 or False values.""" + return html.escape("" if value is None else str(value), quote=True) diff --git a/scripts/import_output_review_decisions.py b/scripts/import_output_review_decisions.py new file mode 100644 index 0000000..2a4a833 --- /dev/null +++ b/scripts/import_output_review_decisions.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python3 +import argparse +import csv +import json +from datetime import date +from pathlib import Path +from typing import Any + +from adjudicate_output_review import ( + DEFAULT_ANSWER_KEY, + DEFAULT_BLIND_PACK, + DEFAULT_DECISIONS, + DEFAULT_OUTPUT_JSON, + DEFAULT_OUTPUT_MD, + adjudicate_output_review, + build_decision_template, + default_reviewer_attestation, + normalize_variant, + pair_index, + review_integrity, +) +from output_review_privacy import forbidden_decision_field_paths + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Imports human blind A/B reviewer decisions into the canonical output-review decision file." + + +def display_path(path: Path) -> str: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +def load_json(path: Path) -> tuple[Any, list[str]]: + try: + return json.loads(path.read_text(encoding="utf-8")), [] + except json.JSONDecodeError as exc: + return None, [f"Invalid JSON file {display_path(path)}: {exc}"] + + +def detect_format(path: Path, requested: str) -> str: + if requested != "auto": + return requested + suffix = path.suffix.lower() + if suffix == ".jsonl": + return "jsonl" + if suffix == ".csv": + return "csv" + return "json" + + +def read_decision_source(path: Path, source_format: str) -> tuple[dict[str, Any], list[dict[str, Any]], list[str]]: + if not path.exists(): + return {}, [], [f"Missing decision source: {display_path(path)}"] + if source_format == "json": + payload, failures = load_json(path) + if failures: + return {}, [], failures + if isinstance(payload, list): + return {}, payload, [] + if not isinstance(payload, dict): + return {}, [], ["JSON decision source must be an object or list"] + decisions = payload.get("decisions", []) + if not isinstance(decisions, list): + return payload, [], ["decisions must be a list"] + return payload, decisions, [] + if source_format == "jsonl": + decisions: list[dict[str, Any]] = [] + failures: list[str] = [] + for line_number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1): + if not line.strip(): + continue + try: + item = json.loads(line) + except json.JSONDecodeError as exc: + failures.append(f"JSONL line {line_number} is invalid: {exc}") + continue + if not isinstance(item, dict): + failures.append(f"JSONL line {line_number} must be an object") + continue + decisions.append(item) + return {}, decisions, failures + if source_format == "csv": + with path.open("r", encoding="utf-8", newline="") as handle: + return {}, [dict(row) for row in csv.DictReader(handle)], [] + return {}, [], [f"Unsupported decision source format: {source_format}"] + + +def known_case_ids(blind_pack_path: Path) -> tuple[set[str], list[str]]: + payload, failures = load_json(blind_pack_path) + if failures: + return set(), failures + if not isinstance(payload, dict): + return set(), [f"Blind pack root must be an object: {display_path(blind_pack_path)}"] + pairs = pair_index(payload) + if not pairs: + return set(), [f"Blind pack has no pairs: {display_path(blind_pack_path)}"] + return set(pairs), [] + + +def parse_confidence(value: Any, case_id: str) -> tuple[float | None, str | None]: + if value is None or str(value).strip() == "": + return None, None + try: + parsed = float(value) + except (TypeError, ValueError): + return None, f"{case_id}: confidence must be numeric" + if parsed < 0 or parsed > 1: + return None, f"{case_id}: confidence must be between 0 and 1" + return round(parsed, 3), None + + +def normalize_decisions( + source_items: list[dict[str, Any]], + case_ids: set[str], +) -> tuple[list[dict[str, Any]], list[str]]: + failures: list[str] = [] + normalized: list[dict[str, Any]] = [] + seen: set[str] = set() + for index, item in enumerate(source_items, start=1): + if not isinstance(item, dict): + failures.append(f"decision #{index} must be an object") + continue + blocked_fields = forbidden_decision_field_paths(item, f"decision #{index}") + if blocked_fields: + failures.append(f"decision #{index} contains forbidden raw or answer-key fields: {', '.join(blocked_fields)}") + case_id = str(item.get("case_id", "")).strip() + if not case_id: + failures.append(f"decision #{index} is missing case_id") + continue + if case_id not in case_ids: + failures.append(f"decision #{index} references unknown case_id: {case_id}") + if case_id in seen: + failures.append(f"duplicate decision for case_id: {case_id}") + seen.add(case_id) + winner = normalize_variant(item.get("winner_variant", item.get("winner", item.get("reviewer_winner_variant", "")))) + if winner and winner not in {"A", "B"}: + failures.append(f"{case_id}: winner_variant must be A or B") + confidence, confidence_failure = parse_confidence(item.get("confidence"), case_id) + if confidence_failure: + failures.append(confidence_failure) + reason = str(item.get("reason", "")).strip() + if winner in {"A", "B"} and not reason: + failures.append(f"{case_id}: reason is required for imported human decisions") + normalized.append( + { + "case_id": case_id, + "winner_variant": winner, + "confidence": confidence, + "reason": reason, + } + ) + return normalized, failures + + +def canonical_payload( + blind_pack_path: Path, + source_path: Path, + source_format: str, + reviewer: str, + reviewed_at: str, + decisions: list[dict[str, Any]], + reviewer_attestation: dict[str, Any], +) -> dict[str, Any]: + blind_pack = json.loads(blind_pack_path.read_text(encoding="utf-8")) + template = build_decision_template(blind_pack) + completed = sum(1 for item in decisions if item.get("winner_variant") in {"A", "B"}) + return { + "schema_version": "1.0", + "reviewer": reviewer, + "reviewed_at": reviewed_at, + "review_integrity": review_integrity(blind_pack), + "reviewer_attestation": reviewer_attestation, + "decision_contract": template["decision_contract"], + "import_contract": { + "schema_version": "1.0", + "source_path": display_path(source_path), + "source_format": source_format, + "raw_content_allowed": False, + "answer_key_fields_allowed": False, + "answer_key_opened_by_importer": False, + "all_or_nothing_write": True, + "decision_count": len(decisions), + "completed_decision_count": completed, + "pending_decision_count": len(decisions) - completed, + "blind_review_attestation_supported": True, + "blind_review_attested": reviewer_attestation.get("blind_review_completed_before_answer_key") is True + and reviewer_attestation.get("answer_key_not_opened_before_decisions") is True, + }, + "decisions": decisions, + } + + +def normalize_reviewer_attestation(source_meta: dict[str, Any], blind_review_attested: bool) -> dict[str, Any]: + attestation = default_reviewer_attestation() + source_attestation = source_meta.get("reviewer_attestation", {}) + if isinstance(source_attestation, dict): + for key in attestation: + if source_attestation.get(key) is True: + attestation[key] = True + elif source_attestation.get(key) is False: + attestation[key] = False + if blind_review_attested: + attestation["blind_review_completed_before_answer_key"] = True + attestation["answer_key_not_opened_before_decisions"] = True + return attestation + + +def import_output_review_decisions( + source_path: Path, + blind_pack_path: Path, + output_json: Path, + source_format: str = "auto", + reviewer: str = "", + reviewed_at: str = "", + run_adjudication: bool = False, + answer_key_path: Path = DEFAULT_ANSWER_KEY, + adjudication_json: Path = DEFAULT_OUTPUT_JSON, + adjudication_md: Path = DEFAULT_OUTPUT_MD, + blind_review_attested: bool = False, +) -> dict[str, Any]: + detected_format = detect_format(source_path, source_format) + source_meta, source_items, failures = read_decision_source(source_path, detected_format) + reviewer = reviewer or str(source_meta.get("reviewer", "")).strip() + reviewed_at = reviewed_at or str(source_meta.get("reviewed_at", "")).strip() + if not reviewer: + failures.append("reviewer is required for imported human decisions") + if not reviewed_at: + failures.append("reviewed_at is required for imported human decisions") + reviewer_attestation = normalize_reviewer_attestation(source_meta, blind_review_attested) + case_ids, case_failures = known_case_ids(blind_pack_path) + failures.extend(case_failures) + decisions, decision_failures = normalize_decisions(source_items, case_ids) + failures.extend(decision_failures) + payload: dict[str, Any] = { + "schema_version": "1.0", + "ok": not failures, + "summary": { + "decision_count": len(decisions), + "completed_decision_count": sum(1 for item in decisions if item.get("winner_variant") in {"A", "B"}), + "pending_decision_count": sum(1 for item in decisions if not item.get("winner_variant")), + "failure_count": len(failures), + "canonical_written": False, + "adjudication_run": False, + }, + "artifacts": { + "source": display_path(source_path), + "blind_pack": display_path(blind_pack_path), + "decisions": display_path(output_json), + "adjudication_json": display_path(adjudication_json), + "adjudication_markdown": display_path(adjudication_md), + }, + "failures": failures, + } + if failures: + return payload + canonical = canonical_payload( + blind_pack_path, + source_path, + detected_format, + reviewer, + reviewed_at, + decisions, + reviewer_attestation, + ) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(canonical, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + payload["summary"]["canonical_written"] = True + payload["canonical"] = canonical + if run_adjudication: + adjudication = adjudicate_output_review( + blind_pack_path=blind_pack_path, + answer_key_path=answer_key_path, + decisions_path=output_json, + output_json=adjudication_json, + output_md=adjudication_md, + ) + payload["summary"]["adjudication_run"] = True + payload["summary"]["adjudication_ok"] = adjudication["ok"] + payload["summary"]["adjudication_pending_count"] = adjudication["summary"]["pending_count"] + payload["summary"]["adjudication_invalid_decision_count"] = adjudication["summary"]["invalid_decision_count"] + payload["adjudication_summary"] = adjudication["summary"] + if not adjudication["ok"]: + payload["ok"] = False + payload["failures"] = list(adjudication.get("failures", [])) + payload["summary"]["failure_count"] = len(payload["failures"]) + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Import blind A/B reviewer decisions into the canonical output-review decision file.") + parser.add_argument("--input", required=True, help="Reviewer decision source in JSON, JSONL, or CSV format.") + parser.add_argument("--format", choices=["auto", "json", "jsonl", "csv"], default="auto") + parser.add_argument("--blind-pack", default=str(DEFAULT_BLIND_PACK)) + parser.add_argument("--output-json", default=str(DEFAULT_DECISIONS)) + parser.add_argument("--reviewer") + parser.add_argument("--reviewed-at", default=date.today().isoformat()) + parser.add_argument( + "--blind-review-attested", + action="store_true", + help="Attest that reviewer choices were completed before the answer key was opened.", + ) + parser.add_argument("--run-adjudication", action="store_true") + parser.add_argument("--answer-key", default=str(DEFAULT_ANSWER_KEY)) + parser.add_argument("--adjudication-json", default=str(DEFAULT_OUTPUT_JSON)) + parser.add_argument("--adjudication-md", default=str(DEFAULT_OUTPUT_MD)) + args = parser.parse_args() + payload = import_output_review_decisions( + source_path=Path(args.input).resolve(), + blind_pack_path=Path(args.blind_pack).resolve(), + output_json=Path(args.output_json).resolve(), + source_format=args.format, + reviewer=args.reviewer or "", + reviewed_at=args.reviewed_at or "", + run_adjudication=args.run_adjudication, + answer_key_path=Path(args.answer_key).resolve(), + adjudication_json=Path(args.adjudication_json).resolve(), + adjudication_md=Path(args.adjudication_md).resolve(), + blind_review_attested=args.blind_review_attested, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/import_telemetry_events.py b/scripts/import_telemetry_events.py new file mode 100644 index 0000000..d2802fe --- /dev/null +++ b/scripts/import_telemetry_events.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path +from typing import Any + +from render_adoption_drift_report import display_path, normalize_event, render_report, skill_defaults + + +def load_candidate_events( + input_jsonl: Path, + defaults: dict[str, str], + default_source: str, + default_command: str, +) -> tuple[list[dict[str, str]], list[str]]: + events: list[dict[str, str]] = [] + failures: list[str] = [] + for index, line in enumerate(input_jsonl.read_text(encoding="utf-8").splitlines(), start=1): + if not line.strip(): + continue + try: + raw = json.loads(line) + except json.JSONDecodeError as exc: + failures.append(f"line {index}: invalid JSONL event: {exc.msg}") + continue + if not isinstance(raw, dict): + failures.append(f"line {index}: telemetry event must be a JSON object") + continue + raw.setdefault("source", default_source) + raw.setdefault("command", default_command) + event, event_failures = normalize_event(raw, defaults, f"line {index}") + failures.extend(event_failures) + if event: + events.append(event) + return events, failures + + +def append_events(events_jsonl: Path, events: list[dict[str, str]]) -> None: + events_jsonl.parent.mkdir(parents=True, exist_ok=True) + with events_jsonl.open("a", encoding="utf-8") as handle: + for event in events: + handle.write(json.dumps(event, ensure_ascii=False, sort_keys=True) + "\n") + + +def import_events( + skill_dir: Path, + input_jsonl: Path, + events_jsonl: Path | None = None, + output_json: Path | None = None, + output_md: Path | None = None, + generated_at: str | None = None, + default_source: str = "external", + default_command: str = "external-client", + dry_run: bool = False, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + input_jsonl = input_jsonl.resolve() + reports_dir = skill_dir / "reports" + events_jsonl = (events_jsonl or reports_dir / "telemetry_events.jsonl").resolve() + output_json = (output_json or reports_dir / "adoption_drift_report.json").resolve() + output_md = (output_md or reports_dir / "adoption_drift_report.md").resolve() + failures: list[str] = [] + if not input_jsonl.exists(): + failures.append(f"Input telemetry JSONL does not exist: {display_path(input_jsonl)}") + candidate_events: list[dict[str, str]] = [] + else: + candidate_events, load_failures = load_candidate_events( + input_jsonl, + skill_defaults(skill_dir), + default_source, + default_command, + ) + failures.extend(load_failures) + + adoption_report: dict[str, Any] | None = None + if not failures and not dry_run: + append_events(events_jsonl, candidate_events) + adoption_report = render_report( + skill_dir, + events_jsonl=events_jsonl, + output_json=output_json, + output_md=output_md, + generated_at=generated_at, + ) + + return { + "ok": not failures, + "schema_version": "1.0", + "skill_dir": display_path(skill_dir), + "input_jsonl": display_path(input_jsonl), + "events_jsonl": display_path(events_jsonl), + "dry_run": dry_run, + "imported_count": 0 if failures or dry_run else len(candidate_events), + "candidate_count": len(candidate_events), + "failures": failures, + "imported_preview": candidate_events[:5], + "artifacts": { + "events_jsonl": display_path(events_jsonl), + "adoption_drift_json": display_path(output_json), + "adoption_drift_md": display_path(output_md), + }, + "adoption_drift": { + "summary": adoption_report.get("summary", {}) if adoption_report else {}, + "artifacts": adoption_report.get("artifacts", {}) if adoption_report else {}, + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Import external metadata-only telemetry into a local skill event stream.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--input-jsonl", required=True) + parser.add_argument("--events-jsonl") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--generated-at") + parser.add_argument("--source", choices=["external", "manual", "unknown", "yao_cli"], default="external") + parser.add_argument("--command", default="external-client") + parser.add_argument("--dry-run", action="store_true") + args = parser.parse_args() + + report = import_events( + Path(args.skill_dir), + Path(args.input_jsonl), + events_jsonl=Path(args.events_jsonl) if args.events_jsonl else None, + output_json=Path(args.output_json) if args.output_json else None, + output_md=Path(args.output_md) if args.output_md else None, + generated_at=args.generated_at, + default_source=args.source, + default_command=args.command, + dry_run=args.dry_run, + ) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/init_skill.py b/scripts/init_skill.py new file mode 100644 index 0000000..ff67e8f --- /dev/null +++ b/scripts/init_skill.py @@ -0,0 +1,486 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from datetime import date +from pathlib import Path + +from compile_skill import render_compile_report +from export_skill_ir import build_skill_ir, validate_ir +from github_benchmark_scan import run_github_benchmark_scan +from render_intent_confidence import render_intent_confidence +from render_intent_dialogue import render_intent_dialogue +from render_iteration_directions import render_iteration_directions +from render_adoption_drift_report import render_report as render_adoption_drift_report +from render_review_annotations import render_report as render_review_annotations +from render_review_waivers import render_report as render_review_waivers +from render_artifact_design_profile import render_artifact_design_profile +from render_output_risk_profile import render_output_risk_profile +from render_prompt_quality_profile import render_prompt_quality_profile +from render_reference_scan import parse_reference, render_reference_scan +from render_reference_synthesis import render_reference_synthesis +from render_review_studio import render_review_studio +from render_review_viewer import render_review_viewer +from render_skill_interpretation import render_skill_interpretation +from render_skill_overview import render_skill_overview +from render_system_model import render_system_model + + +SKILL_TEMPLATE = """--- +name: {name} +description: {description} +--- + +# {title} + +## Workflow + +1. Understand the request. +2. Execute the main task. +3. Validate the result. + +## Output Quality Guardrails + +- Before final output, apply the likely failure modes in `reports/output-risk-profile.md` when that report is present. +- Before rendering reports, tutorials, review pages, dashboards, or visual artifacts, apply the artifact direction and visual quality gates in `reports/artifact-design-profile.md` when that report is present. +- When prompt behavior, role design, dialogue quality, or output contracts matter, apply `reports/prompt-quality-profile.md` when that report is present. +- Before adding more structure, apply the boundary, feedback-loop, drift, and leverage-point checks in `reports/system-model.md` when that report is present. +- Repair generic headings, cluttered notes, fragile visual assumptions, weak tables, and missing verification cues before handing work back. +- Map role, task, and format into skill behavior rather than copying a large prompt template into `SKILL.md`. +- Let the artifact's content choose the visual system; do not copy a fixed palette or report style from another skill without a clear reason. +- If output-specific evidence is missing, state the gap instead of inventing screenshots, citations, data, or examples. + +## Honest Boundaries + +- Use this skill for the recurring job described in the trigger, not for one-off adjacent requests. +- Treat missing inputs, unclear outputs, or conflicting constraints as reasons to ask one focused clarification. +- Do not add new references, scripts, evals, or governance unless they improve reliability more than they add weight. +""" + + +README_TEMPLATE = """# {title} + +## What It Does + +`{name}` is a reusable skill package for this job: + +> {description} + +## How To Use + +1. Load the skill through `SKILL.md`. +2. Start with `reports/intent-dialogue.md` to tighten the real job, outputs, exclusions, and the standards you care about. +3. Open `reports/reference-scan.md` to capture external benchmarks and any user-supplied references worth learning from. +4. Review `reports/intent-confidence.md` to see whether the real job, inputs, outputs, and exclusions are clear enough yet. +5. Open `reports/reference-synthesis.md` to see the GitHub benchmarks plus curated official, research, and principle tracks in one place. +6. Follow the workflow steps in `SKILL.md`. +7. Open `reports/skill-interpretation.html` first for the first-class bilingual interpretation report: role, principle, scenarios, trigger, inputs, outputs, highlights, risks, assets, and upgrade directions. It defaults to Simplified Chinese and includes an English switch in the top right. +8. Check `reports/skill-overview.html` for the generated bilingual HTML skill audit report: overview, metrics, capability profile, principle, contract, quality, risk, assets, and iteration roadmap. +9. Open `reports/review-studio.html` for the one-page Review Studio 2.0 gate view. +10. Record source-line reviewer comments in `reports/review_annotations.md` when review needs follow-up. +11. Open `reports/review-viewer.html` for a compact visual review of the package. +12. Check `reports/output-risk-profile.md` to see likely output mistakes and self-repair checks. +13. Check `reports/artifact-design-profile.md` to see the intended artifact direction, layout patterns, visual quality gates, and anti-patterns. +14. Check `reports/prompt-quality-profile.md` to see the need model, RTF-to-skill mapping, complexity, and prompt-facing quality matrix. +15. Review `reports/skill-ir.json` for the platform-neutral Skill IR contract before platform-specific packaging. +16. Review `reports/compiled_targets.md` to see how Skill IR compiles into OpenAI, Claude, generic, and Agent Skills compatible target contracts. +17. Review `reports/iteration-directions.md` for the three most valuable next moves. +18. Review `reports/system-model.md` to understand the boundary, feedback loops, drift watch, failure map, and highest-leverage next changes. +19. Review `reports/adoption_drift_report.md` to see local-first metadata-only adoption and drift signals. +20. Review `reports/review_waivers.md` to see human reviewer risk approvals and expiry dates. + +## Honest Boundaries + +- This package starts from the current intent frame and should not pretend to cover unclear adjacent jobs. +- The first version should ask for clarification when the real input, output, or exclusion boundary is still fuzzy. +- New structure should be added only when it earns its keep through evidence, validation, or reviewer need. + +## Package Map + +- `SKILL.md`: trigger and workflow entrypoint +- `agents/interface.yaml`: portable interface metadata +- `manifest.json`: lifecycle and packaging metadata +- `reports/intent-dialogue.md`: front-loaded discovery questions for better boundary design and clearer human alignment +- `reports/intent-confidence.md`: current clarity score, open gaps, and the next follow-up questions worth asking +- `reports/github-benchmark-scan.md`: top public benchmark repositories, extracted patterns, and borrow or avoid notes +- `reports/reference-scan.md`: benchmark notes from public references, user references, and local constraints +- `reports/reference-synthesis.md`: a combined view of GitHub benchmarks plus curated world-class pattern tracks +- `reports/output-risk-profile.md`: predicted output failure modes and self-repair constraints for this skill +- `reports/artifact-design-profile.md`: artifact-specific design direction, layout patterns, visual quality gates, and anti-patterns +- `reports/prompt-quality-profile.md`: prompt-facing need model, RTF mapping, complexity, and quality matrix +- `reports/system-model.md`: systems-thinking model for boundary, feedback loops, drift, failure patterns, and leverage points +- `reports/skill-ir.json`: platform-neutral 2.0 Skill IR contract for trigger, workflow, resources, evals, risk, and governance +- `reports/compiled_targets.md`: target compiler report showing generated contracts, adapter modes, preserved semantics, warnings, and unsupported features +- `reports/skill-interpretation.html`: first-class bilingual interpretation report for role, principle, scenarios, trigger, inputs, outputs, quality evidence, risks, assets, highlights, and upgrade directions +- `reports/skill-overview.html`: white-background bilingual HTML skill audit report with sticky four-character Chinese navigation, a top-right language switch, metrics, SVG charts, contract boundary, quality review, risk governance, assets, and iteration roadmap +- `reports/review-studio.html`: Review Studio 2.0 gate page for intent, trigger, output eval, context, runtime conformance, trust, atlas, and release readiness +- `reports/review-viewer.html`: compact review page for architecture, usage, feedback, and next steps +- `reports/iteration-directions.md`: the top three next iteration directions +- `reports/adoption_drift_report.md`: local-first metadata-only telemetry summary for adoption, missed triggers, bad outputs, script errors, and review drift +- `reports/review_waivers.md`: human reviewer risk approval ledger for warning acceptance and expiry +- `reports/review_annotations.md`: source-line reviewer comments linked to Review Studio gates +""" + + +INTERFACE_TEMPLATE = """interface: + display_name: "{title}" + short_description: "{short_description}" + default_prompt: "Use ${name} when you need to {default_prompt}." +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "openai" + - "claude" + - "generic" + - "vscode" + activation: + mode: "manual" + paths: [] + execution: + context: "inline" + shell: "bash" + trust: + source_tier: "local" + remote_inline_execution: "forbid" + remote_metadata_policy: "allow-metadata-only" + degradation: + openai: "metadata-adapter" + claude: "neutral-source-plus-adapter" + generic: "neutral-source" + vscode: "agent-skills-source-with-vscode-notes" +""" + + +MODE_CONFIG = { + "scaffold": { + "maturity_tier": "scaffold", + "lifecycle_stage": "scaffold", + "context_budget_tier": "scaffold", + "review_cadence": "per-release", + }, + "production": { + "maturity_tier": "production", + "lifecycle_stage": "production", + "context_budget_tier": "production", + "review_cadence": "monthly", + }, + "library": { + "maturity_tier": "library", + "lifecycle_stage": "library", + "context_budget_tier": "library", + "review_cadence": "quarterly", + }, + "governed": { + "maturity_tier": "governed", + "lifecycle_stage": "governed", + "context_budget_tier": "governed", + "review_cadence": "monthly", + }, +} + + +SKILL_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9_-]*$") + + +def validate_skill_name(name: str) -> None: + if not SKILL_NAME_RE.fullmatch(name): + raise ValueError( + "Invalid skill name. Use a slug matching ^[a-z0-9][a-z0-9_-]*$; path separators and absolute paths are not allowed." + ) + + +def resolve_skill_root(output_dir: str, name: str) -> Path: + validate_skill_name(name) + output_root = Path(output_dir).resolve() + root = (output_root / name).resolve() + try: + root.relative_to(output_root) + except ValueError as exc: + raise ValueError(f"Skill root escapes output directory: {root}") from exc + return root + + +def build_manifest(name: str, mode: str, archetype: str) -> dict: + mode_payload = MODE_CONFIG.get(mode, MODE_CONFIG["scaffold"]) + return { + "name": name, + "version": "0.1.0", + "owner": "Yao Team", + "updated_at": str(date.today()), + "status": "active", + "maturity_tier": mode_payload["maturity_tier"], + "lifecycle_stage": mode_payload["lifecycle_stage"], + "context_budget_tier": mode_payload["context_budget_tier"], + "review_cadence": mode_payload["review_cadence"], + "skill_archetype": archetype, + "target_platforms": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode", + ], + "factory_components": [ + "references", + "scripts", + "reports", + ], + } + + +def build_report_view(artifacts: dict) -> dict: + html_report = artifacts.get("skill_overview_html", "") + json_report = artifacts.get("skill_overview_json", "") + interpretation_report = artifacts.get("skill_interpretation_html", "") + system_model = artifacts.get("system_model_md", "") + review_studio = artifacts.get("review_studio_html", "") + return { + "title": "Skill 总结报告", + "html_report": html_report, + "json_report": json_report, + "interpretation_report": interpretation_report, + "system_model": system_model, + "review_studio": review_studio, + "message": ( + f"Skill 已创建完成。建议先打开解读报告:{interpretation_report};再查看总结报告:{html_report}。" + "解读报告会用中文简体默认展示这个 Skill 的作用、原理、使用场景、触发方式、输入输出、亮点和后续升级方向;" + "总结报告会展示概述、指标、原理、触发边界、输入输出、目标编译、质量评估、风险治理、包体资产和升级路线;" + f"然后打开 Review Studio 2.0:{review_studio},检查意图、触发、输出评测、运行一致性、信任和发布闸门。" + "后续 reviewer 的文件级或行级意见可以记录到 reports/review_annotations.md。" + "如需审查平台适配细节,请打开 reports/compiled_targets.md。" + "报告默认使用中文简体,右上角可以切换英文版。" + ), + "next_action": "Open reports/skill-interpretation.html before editing more files.", + } + + +def render_skill_ir(root: Path) -> dict: + payload = build_skill_ir(root) + failures = validate_ir(payload) + output_json = root / "reports" / "skill-ir.json" + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return { + "ok": not failures, + "artifacts": {"json": str(output_json)}, + "summary": { + "name": payload.get("name"), + "maturity": payload.get("governance", {}).get("maturity"), + "target_count": len(payload.get("targets", [])), + "trigger_samples": len(payload.get("trigger_surface", {}).get("should_trigger", [])), + }, + "failures": failures, + } + + +def absolute_skill_artifact(root: Path, value: str) -> str: + path = Path(value) + return str(path if path.is_absolute() else root / path) + + +def dedupe_references(references: list[dict]) -> list[dict]: + seen = set() + deduped = [] + for reference in references: + key = ( + reference.get("source"), + reference.get("name"), + reference.get("category"), + reference.get("borrow"), + reference.get("avoid"), + ) + if key in seen: + continue + seen.add(key) + deduped.append(reference) + return deduped + + +def initialize_skill( + name: str, + description: str, + title: str | None = None, + output_dir: str = ".", + mode: str = "scaffold", + archetype: str = "scaffold", + external_references: list[dict] | None = None, + user_references: list[dict] | None = None, + local_constraints: list[dict] | None = None, + github_query: str | None = None, + github_top_n: int = 3, + github_fixture_dir: str | None = None, + intent_context: dict | None = None, +) -> dict: + title = title or name.replace("-", " ").title() + root = resolve_skill_root(output_dir, name) + (root / "agents").mkdir(parents=True, exist_ok=True) + (root / "references").mkdir(exist_ok=True) + (root / "scripts").mkdir(exist_ok=True) + (root / "reports").mkdir(exist_ok=True) + (root / "SKILL.md").write_text( + SKILL_TEMPLATE.format(name=name, description=json.dumps(description, ensure_ascii=False), title=title), + encoding="utf-8", + ) + (root / "README.md").write_text( + README_TEMPLATE.format(name=name, description=description, title=title), + encoding="utf-8", + ) + (root / "agents" / "interface.yaml").write_text( + INTERFACE_TEMPLATE.format( + name=name, + title=title, + short_description=description[:80], + default_prompt=description.rstrip("."), + ), + encoding="utf-8", + ) + (root / "manifest.json").write_text( + json.dumps(build_manifest(name, mode, archetype), ensure_ascii=False, indent=2), + encoding="utf-8", + ) + intent_confidence = render_intent_confidence(root, context=intent_context) + intent_dialogue = render_intent_dialogue(root) + benchmark_scan = None + combined_external_references = [*(external_references or [])] + if github_query: + benchmark_scan = run_github_benchmark_scan( + root, + query=github_query, + top_n=github_top_n, + fixture_dir=Path(github_fixture_dir).resolve() if github_fixture_dir else None, + ) + combined_external_references = [*benchmark_scan.get("external_references", []), *combined_external_references] + reference_scan = render_reference_scan( + root, + dedupe_references([*combined_external_references, *(user_references or []), *(local_constraints or [])]), + ) + reference_synthesis = render_reference_synthesis(root) + output_risk_profile = render_output_risk_profile(root) + artifact_design_profile = render_artifact_design_profile(root) + prompt_quality_profile = render_prompt_quality_profile(root) + system_model = render_system_model(root) + skill_ir = render_skill_ir(root) + compiled_targets = render_compile_report(root) + iteration_directions = render_iteration_directions(root) + adoption_drift = render_adoption_drift_report(root) + review_waivers = render_review_waivers(root) + review_annotations = render_review_annotations(root) + overview = render_skill_overview(root) + interpretation = render_skill_interpretation(root) + review_viewer = render_review_viewer(root) + review_studio = render_review_studio(root) + artifacts = { + "readme": str(root / "README.md"), + "manifest": str(root / "manifest.json"), + "intent_confidence_md": intent_confidence["artifacts"]["markdown"], + "intent_confidence_json": intent_confidence["artifacts"]["json"], + "intent_context_json": intent_confidence["artifacts"]["context_json"], + "skill_overview_html": overview["artifacts"]["html"], + "skill_overview_json": overview["artifacts"]["json"], + "skill_interpretation_html": interpretation["artifacts"]["html"], + "skill_interpretation_json": interpretation["artifacts"]["json"], + "intent_dialogue_md": intent_dialogue["artifacts"]["markdown"], + "intent_dialogue_json": intent_dialogue["artifacts"]["json"], + "reference_scan_md": reference_scan["artifacts"]["markdown"], + "reference_scan_json": reference_scan["artifacts"]["json"], + "reference_synthesis_md": reference_synthesis["artifacts"]["markdown"], + "reference_synthesis_json": reference_synthesis["artifacts"]["json"], + "output_risk_profile_md": output_risk_profile["artifacts"]["markdown"], + "output_risk_profile_json": output_risk_profile["artifacts"]["json"], + "artifact_design_profile_md": artifact_design_profile["artifacts"]["markdown"], + "artifact_design_profile_json": artifact_design_profile["artifacts"]["json"], + "prompt_quality_profile_md": prompt_quality_profile["artifacts"]["markdown"], + "prompt_quality_profile_json": prompt_quality_profile["artifacts"]["json"], + "system_model_md": system_model["artifacts"]["markdown"], + "system_model_json": system_model["artifacts"]["json"], + "skill_ir_json": skill_ir["artifacts"]["json"], + "compiled_targets_md": compiled_targets["artifacts"]["markdown"], + "compiled_targets_json": compiled_targets["artifacts"]["json"], + "iteration_directions_md": iteration_directions["artifacts"]["markdown"], + "iteration_directions_json": iteration_directions["artifacts"]["json"], + "adoption_drift_md": adoption_drift["artifacts"]["markdown"], + "adoption_drift_json": adoption_drift["artifacts"]["json"], + "review_waivers_md": review_waivers["artifacts"]["markdown"], + "review_waivers_json": review_waivers["artifacts"]["json"], + "review_annotations_md": review_annotations["artifacts"]["markdown"], + "review_annotations_json": review_annotations["artifacts"]["json"], + "review_viewer_html": review_viewer["artifacts"]["html"], + "review_viewer_json": review_viewer["artifacts"]["json"], + "review_studio_html": absolute_skill_artifact(root, review_studio["artifacts"]["html"]), + "review_studio_json": absolute_skill_artifact(root, review_studio["artifacts"]["json"]), + } + if benchmark_scan is not None: + artifacts["github_benchmark_scan_md"] = benchmark_scan["artifacts"]["markdown"] + artifacts["github_benchmark_scan_json"] = benchmark_scan["artifacts"]["json"] + report_view = build_report_view(artifacts) + return { + "ok": True, + "root": str(root), + "mode": mode, + "archetype": archetype, + "github_benchmark_scan": benchmark_scan, + "intent_confidence": intent_confidence["summary"], + "reference_synthesis": reference_synthesis["summary"], + "system_model": system_model["summary"], + "skill_ir": skill_ir["summary"], + "compiled_targets": compiled_targets["summary"], + "artifacts": artifacts, + "report_view": report_view, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Initialize a minimal skill package.") + parser.add_argument("name", help="skill folder and frontmatter name") + parser.add_argument("--description", default="Describe what the skill does and when to use it.") + parser.add_argument("--title", default=None) + parser.add_argument("--output-dir", default=".") + parser.add_argument("--mode", choices=sorted(MODE_CONFIG.keys()), default="scaffold") + parser.add_argument("--archetype", choices=sorted(MODE_CONFIG.keys()), default="scaffold") + parser.add_argument("--external-reference", action="append", default=[]) + parser.add_argument("--user-reference", action="append", default=[]) + parser.add_argument("--local-constraint", action="append", default=[]) + parser.add_argument("--github-query") + parser.add_argument("--github-top-n", type=int, default=3) + parser.add_argument("--github-fixture-dir") + parser.add_argument("--intent-job") + parser.add_argument("--intent-real-input", action="append", default=[]) + parser.add_argument("--intent-primary-output") + parser.add_argument("--intent-exclusion", action="append", default=[]) + parser.add_argument("--intent-constraint", action="append", default=[]) + parser.add_argument("--intent-standard", action="append", default=[]) + parser.add_argument("--intent-correction", default="") + args = parser.parse_args() + try: + result = initialize_skill( + args.name, + args.description, + args.title, + args.output_dir, + args.mode, + args.archetype, + external_references=[parse_reference(item, "external") for item in args.external_reference], + user_references=[parse_reference(item, "user") for item in args.user_reference], + local_constraints=[parse_reference(item, "local") for item in args.local_constraint], + github_query=args.github_query, + github_top_n=args.github_top_n, + github_fixture_dir=args.github_fixture_dir, + intent_context={ + "job": args.intent_job or args.description, + "real_inputs": args.intent_real_input, + "primary_output": args.intent_primary_output or "", + "description": args.description, + "exclusions": args.intent_exclusion, + "constraints": args.intent_constraint, + "standards": args.intent_standard, + "correction": args.intent_correction, + "user_references": [parse_reference(item, "user")["name"] for item in args.user_reference], + }, + ) + except ValueError as exc: + print(json.dumps({"ok": False, "failures": [str(exc)]}, ensure_ascii=False, indent=2)) + raise SystemExit(2) from exc + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/judge_blind_eval.py b/scripts/judge_blind_eval.py new file mode 100644 index 0000000..a3ff33d --- /dev/null +++ b/scripts/judge_blind_eval.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from trigger_eval import ( + collect_concept_hits, + desired_positive_concepts, + extract_description, + iter_case_items, + load_json, + load_semantic_config, + normalize, + phrase_present, + words, +) + + +DEFAULT_CONFIG_PATH = Path("evals/semantic_config.json") + + +def phrase_hits(text: str, phrases: list[str]) -> list[str]: + normalized = normalize(text) + return [phrase for phrase in phrases if phrase_present(normalized, phrase)] + + +def judge_prompt(description: str, prompt: str, config: dict) -> tuple[bool, dict]: + hints = config.get("optimizer_hints", {}) + generic_exclusion_phrases = [ + "explain", + "summarize", + "translate", + "brainstorm", + "teach me", + "plain english", + ] + desired = desired_positive_concepts(description, config) + positive_hits = collect_concept_hits(prompt, config["positive_concepts"]) + negative_hits = collect_concept_hits(prompt, config["negative_concepts"]) + + focused_positive = [name for name in desired if name in positive_hits] + strong_positive = [ + name + for name in focused_positive + if config["positive_concepts"][name]["weight"] >= 0.16 + ] + trigger_action_hits = phrase_hits(prompt, hints.get("trigger_actions", [])) + input_hits = phrase_hits(prompt, hints.get("inputs", [])) + artifact_hits = phrase_hits(prompt, hints.get("artifacts", [])) + exclusion_hits = phrase_hits(prompt, hints.get("exclusions", [])) + generic_exclusion_hits = phrase_hits(prompt, generic_exclusion_phrases) + + capability_words = words(hints.get("capability", "")) + prompt_words = words(prompt) + capability_overlap = len(capability_words & prompt_words) + + exclusive_negative = sorted( + name for name, hit in negative_hits.items() if hit.get("exclusive") + ) + nonexclusive_negative = sorted( + name for name, hit in negative_hits.items() if not hit.get("exclusive") + ) + + positive_vote = 0 + if trigger_action_hits: + positive_vote += 2 + if len(strong_positive) >= 2: + positive_vote += 2 + elif len(strong_positive) == 1: + positive_vote += 1 + if len(focused_positive) >= 3: + positive_vote += 1 + if input_hits or artifact_hits: + positive_vote += 1 + if capability_overlap >= 1: + positive_vote += 1 + + negative_vote = (2 * len(exclusive_negative)) + len(nonexclusive_negative) + if exclusion_hits: + negative_vote += 1 + if generic_exclusion_hits: + negative_vote += 1 + + trigger = positive_vote >= 3 and positive_vote > negative_vote + if exclusive_negative and positive_vote < 5: + trigger = False + if exclusion_hits and positive_vote < 4: + trigger = False + if generic_exclusion_hits and positive_vote < 4: + trigger = False + + margin = positive_vote - negative_vote + confidence = 0.5 + (0.08 * max(0, margin)) + if exclusive_negative: + confidence += 0.08 + if trigger_action_hits and len(strong_positive) >= 1: + confidence += 0.06 + confidence = max(0.0, min(1.0, confidence)) + + detail = { + "mode": "judge-rubric", + "focused_positive_concepts": focused_positive, + "strong_positive_concepts": strong_positive, + "trigger_action_hits": trigger_action_hits, + "input_hits": input_hits, + "artifact_hits": artifact_hits, + "capability_overlap": capability_overlap, + "exclusive_negative_concepts": exclusive_negative, + "nonexclusive_negative_concepts": nonexclusive_negative, + "exclusion_hits": exclusion_hits, + "generic_exclusion_hits": generic_exclusion_hits, + "positive_vote": positive_vote, + "negative_vote": negative_vote, + "margin": margin, + "confidence": round(confidence, 3), + "concept_evidence": { + "positive": { + name: positive_hits[name]["matched_phrases"] + for name in sorted(positive_hits) + }, + "negative": { + name: negative_hits[name]["matched_phrases"] + for name in sorted(negative_hits) + }, + }, + } + return trigger, detail + + +def classify_bucket(bucket: str) -> bool: + return bucket == "should_trigger" + + +def evaluate_judge(description: str, cases: dict, config: dict) -> dict: + results = {"should_trigger": [], "should_not_trigger": [], "near_neighbor": []} + fp = 0 + fn = 0 + bucket_stats = {} + family_stats: dict[str, dict] = {} + misfires = [] + confidence_total = 0.0 + confidence_count = 0 + + for bucket in ("should_trigger", "should_not_trigger", "near_neighbor"): + expected = classify_bucket(bucket) + items = iter_case_items(cases, bucket) + total = 0 + passed_count = 0 + for item in items: + prompt = item["text"] + family = item.get("family", "default") + predicted, detail = judge_prompt(description, prompt, config) + passed = predicted == expected + total += 1 + confidence_total += detail["confidence"] + confidence_count += 1 + if passed: + passed_count += 1 + if not passed and expected: + fn += 1 + if not passed and not expected: + fp += 1 + + record = { + "prompt": prompt, + "family": family, + "predicted_trigger": predicted, + "expected_trigger": expected, + "passed": passed, + "judge_detail": detail, + } + if abs(detail["margin"]) <= 1: + record["boundary_case"] = True + results[bucket].append(record) + + family_bucket = family_stats.setdefault( + family, + {"total": 0, "passed": 0, "false_positives": 0, "false_negatives": 0}, + ) + family_bucket["total"] += 1 + if passed: + family_bucket["passed"] += 1 + elif expected: + family_bucket["false_negatives"] += 1 + else: + family_bucket["false_positives"] += 1 + + if not passed: + misfires.append( + { + "bucket": bucket, + "family": family, + "prompt": prompt, + "reason": "false_negative" if expected else "false_positive", + "focused_positive_concepts": detail["focused_positive_concepts"], + "exclusive_negative_concepts": detail["exclusive_negative_concepts"], + "trigger_action_hits": detail["trigger_action_hits"], + "margin": detail["margin"], + } + ) + + bucket_stats[bucket] = { + "total": total, + "passed": passed_count, + "pass_rate": round(passed_count / total, 3) if total else None, + } + + for family, stats in family_stats.items(): + stats["pass_rate"] = round(stats["passed"] / stats["total"], 3) if stats["total"] else None + + tp = sum(1 for item in results["should_trigger"] if item["predicted_trigger"]) + precision = tp / (tp + fp) if (tp + fp) else None + recall = tp / (tp + fn) if (tp + fn) else None + + return { + "judge": "rubric-blind-v1", + "judge_explanation": ( + "The blind judge uses a rubric rather than the main threshold scorer. " + "It looks for trigger-action evidence, focused capability evidence, and " + "input or artifact evidence, then blocks on explicit exclusion and " + "exclusive negative signals. This acts as an independent second opinion " + "for blind-holdout prompts." + ), + "false_positives": fp, + "false_negatives": fn, + "precision": round(precision, 3) if precision is not None else None, + "recall": round(recall, 3) if recall is not None else None, + "bucket_stats": bucket_stats, + "family_stats": family_stats, + "misfires": misfires, + "results": results, + "judge_summary": { + "agreement_rate": round( + sum(bucket["passed"] for bucket in bucket_stats.values()) + / sum(bucket["total"] for bucket in bucket_stats.values()), + 3, + ) + if bucket_stats + else None, + "mean_confidence": round(confidence_total / confidence_count, 3) + if confidence_count + else None, + "rubric_version": "blind-v1", + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run a rubric-based blind trigger judge.") + parser.add_argument("--description", help="Description string to evaluate") + parser.add_argument("--description-file", help="Read description text from file") + parser.add_argument("--cases", required=True, help="JSON file with blind trigger cases") + parser.add_argument("--semantic-config", default=str(DEFAULT_CONFIG_PATH), help="Semantic config JSON") + args = parser.parse_args() + + description = args.description + if args.description_file: + description = extract_description(Path(args.description_file).read_text(encoding="utf-8")) + if not description: + raise SystemExit("Provide --description or --description-file") + + cases = load_json(Path(args.cases)) + config = load_semantic_config(Path(args.semantic_config)) + report = evaluate_judge(description, cases, config) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if report["false_positives"] > 0 or report["false_negatives"] > 0: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/lint_skill.py b/scripts/lint_skill.py new file mode 100644 index 0000000..779ea14 --- /dev/null +++ b/scripts/lint_skill.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +def main() -> None: + parser = argparse.ArgumentParser(description="Lint a skill package for basic structure issues.") + parser.add_argument("skill_dir") + args = parser.parse_args() + + root = Path(args.skill_dir).resolve() + failures = [] + warnings = [] + + if root.name.lower() != root.name: + warnings.append("Skill directory is not lowercase.") + if " " in root.name: + failures.append("Skill directory contains spaces.") + + skill_md = root / "SKILL.md" + if skill_md.exists(): + text = skill_md.read_text(encoding="utf-8") + lines = text.splitlines() + if len(lines) > 300: + warnings.append("SKILL.md is getting long; consider moving detail into references/.") + for dirname in ("references", "scripts", "evals", "assets"): + path = root / dirname + if path.exists() and not any(child.is_file() for child in path.rglob("*")): + warnings.append(f"{dirname}/ exists but is empty.") + if path.exists() and any(child.is_file() for child in path.rglob("*")) and dirname not in text and dirname.capitalize() not in text: + warnings.append(f"{dirname}/ contains files but is not referenced in SKILL.md.") + + print(json.dumps({"ok": not failures, "failures": failures, "warnings": warnings}, ensure_ascii=False, indent=2)) + if failures: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/local_output_eval_runner.py b/scripts/local_output_eval_runner.py new file mode 100644 index 0000000..b87b0b2 --- /dev/null +++ b/scripts/local_output_eval_runner.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +import argparse +import json +import sys +from typing import Any + + +def estimate_tokens(text: str) -> int: + if not text: + return 0 + return max(1, round(len(text) / 4)) + + +def build_response(request: dict[str, Any], execution_kind: str, provider: str, model: str) -> dict[str, Any]: + output = str(request.get("fixture_output", "")) + prompt = str(request.get("prompt", "")) + input_tokens = estimate_tokens(prompt) + output_tokens = estimate_tokens(output) + response: dict[str, Any] = { + "output": output, + "execution_kind": execution_kind, + "provider": provider, + "usage": { + "input_tokens": input_tokens, + "output_tokens": output_tokens, + "total_tokens": input_tokens + output_tokens, + "estimated": True, + }, + } + if model: + response["model"] = model + return response + + +def main() -> None: + parser = argparse.ArgumentParser( + description=( + "Local deterministic output-eval runner. It executes the runner contract " + "without claiming provider-backed model generation." + ) + ) + parser.add_argument("--execution-kind", choices=["command", "model"], default="command") + parser.add_argument("--provider", default="local-output-eval-runner") + parser.add_argument("--model", default="") + args = parser.parse_args() + + raw = sys.stdin.read() + if not raw.strip(): + print("runner requires a JSON request on stdin", file=sys.stderr) + raise SystemExit(2) + try: + request = json.loads(raw) + except json.JSONDecodeError as exc: + print(f"invalid JSON request: {exc}", file=sys.stderr) + raise SystemExit(2) + if not isinstance(request, dict): + print("runner request must be a JSON object", file=sys.stderr) + raise SystemExit(2) + + response = build_response(request, args.execution_kind, args.provider, args.model) + print(json.dumps(response, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/scripts/optimize_description.py b/scripts/optimize_description.py new file mode 100644 index 0000000..9bfee6d --- /dev/null +++ b/scripts/optimize_description.py @@ -0,0 +1,585 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from context_sizer import estimate_tokens +from description_optimizer_reporting import render_markdown +from judge_blind_eval import evaluate_judge +from trigger_eval import ( + compare_reports, + evaluate, + extract_description, + load_json, + load_semantic_config, +) + + +def read_description(path: Path) -> str: + return extract_description(path.read_text(encoding="utf-8")).strip() + + +def serial_join(items: list[str], conjunction: str = "or") -> str: + items = [item.strip() for item in items if item and item.strip()] + if not items: + return "" + if len(items) == 1: + return items[0] + if len(items) == 2: + return f"{items[0]} {conjunction} {items[1]}" + return f"{', '.join(items[:-1])}, {conjunction} {items[-1]}" + + +def sentence(text: str) -> str: + text = " ".join(text.split()) + if not text: + return text + if text.endswith("."): + return text + return f"{text}." + + +def build_candidates(current: str, config: dict) -> list[dict]: + hints = config.get("optimizer_hints", {}) + capability = hints.get("capability") or current.split(".", 1)[0].strip() + inputs = hints.get("inputs", []) + trigger_actions = hints.get("trigger_actions", []) + exclusions = hints.get("exclusions", []) + artifacts = hints.get("artifacts", []) + + capability_sentence = sentence(capability) + inputs_clause = f" from {serial_join(inputs)}" if inputs else "" + trigger_clause = serial_join(trigger_actions[:3], "or") + exclusion_clause = serial_join(exclusions[:3], "or") + artifact_clause = serial_join(artifacts[:4], "or") + + raw_candidates = [ + { + "id": "current", + "label": "Current", + "description": sentence(current), + "strategy": "current", + }, + ] + + if capability and trigger_clause: + raw_candidates.extend( + [ + { + "id": "balanced", + "label": "Balanced", + "description": sentence(f"{capability}{inputs_clause}. Use when asked to {trigger_clause}"), + "strategy": "balanced_template", + }, + { + "id": "boundary", + "label": "Boundary", + "description": sentence( + f"{capability}{inputs_clause}. Use when asked to {trigger_clause}. Do not use for {exclusion_clause}" + ) + if exclusion_clause + else sentence(f"{capability}{inputs_clause}. Use when asked to {trigger_clause}"), + "strategy": "boundary_template", + }, + { + "id": "minimal", + "label": "Minimal", + "description": sentence(f"{capability}. Use when asked to {trigger_clause}"), + "strategy": "minimal_template", + }, + ] + ) + + if capability and artifact_clause and trigger_clause: + raw_candidates.append( + { + "id": "artifact_aware", + "label": "Artifact Aware", + "description": sentence( + f"{capability}{inputs_clause}. Trigger when requests mention {artifact_clause} and the job is to {trigger_clause}" + ), + "strategy": "artifact_template", + } + ) + + if capability and exclusion_clause: + raw_candidates.append( + { + "id": "guardrail", + "label": "Guardrail", + "description": sentence(f"{capability}{inputs_clause}. Do not use for {exclusion_clause}"), + "strategy": "guardrail_template", + } + ) + + deduped = [] + seen = set() + for candidate in raw_candidates: + normalized = candidate["description"].lower() + if normalized in seen: + continue + seen.add(normalized) + deduped.append(candidate) + return deduped + + +def objective_key(report: dict, token_count: int) -> tuple: + bucket_stats = report.get("bucket_stats", {}) + near_rate = bucket_stats.get("near_neighbor", {}).get("pass_rate") or 0 + negative_rate = bucket_stats.get("should_not_trigger", {}).get("pass_rate") or 0 + precision = report.get("precision") or 0 + recall = report.get("recall") or 0 + return ( + report["false_positives"], + report["false_negatives"], + -near_rate, + -negative_rate, + -precision, + -recall, + token_count, + ) + + +def summarize_candidate(candidate: dict, dev_report: dict, holdout_report: dict | None) -> dict: + token_count = estimate_tokens(candidate["description"]) + summary = { + **candidate, + "estimated_tokens": token_count, + "dev": { + "false_positives": dev_report["false_positives"], + "false_negatives": dev_report["false_negatives"], + "precision": dev_report["precision"], + "recall": dev_report["recall"], + "near_neighbor_pass_rate": dev_report["bucket_stats"]["near_neighbor"]["pass_rate"], + "should_not_trigger_pass_rate": dev_report["bucket_stats"]["should_not_trigger"]["pass_rate"], + }, + "selection_key": objective_key(dev_report, token_count), + } + if holdout_report: + summary["holdout"] = { + "false_positives": holdout_report["false_positives"], + "false_negatives": holdout_report["false_negatives"], + "precision": holdout_report["precision"], + "recall": holdout_report["recall"], + "near_neighbor_pass_rate": holdout_report["bucket_stats"]["near_neighbor"]["pass_rate"], + "should_not_trigger_pass_rate": holdout_report["bucket_stats"]["should_not_trigger"]["pass_rate"], + } + return summary + + +def summarize_gate_report(report: dict | None) -> dict | None: + if not report: + return None + summary = { + "false_positives": report["false_positives"], + "false_negatives": report["false_negatives"], + "precision": report["precision"], + "recall": report["recall"], + "near_neighbor_pass_rate": report["bucket_stats"]["near_neighbor"]["pass_rate"], + "should_not_trigger_pass_rate": report["bucket_stats"]["should_not_trigger"]["pass_rate"], + } + if report.get("judge_summary"): + summary["judge_summary"] = report["judge_summary"] + return summary + + +def error_tuple(report: dict | None) -> tuple[int, int] | None: + if not report: + return None + return (report["false_positives"], report["false_negatives"]) + + +def safe_round(value: float | None) -> float | None: + if value is None: + return None + return round(value, 3) + + +def summarize_family_health(report: dict | None) -> dict | None: + if not report: + return None + + family_stats = report.get("family_stats", {}) + ordered = sorted( + family_stats.items(), + key=lambda item: ( + item[1].get("false_positives", 0) + item[1].get("false_negatives", 0), + item[1].get("pass_rate") or 0, + -(item[1].get("total") or 0), + item[0], + ), + ) + weakest = ordered[-1] if ordered else None + failing = [] + for family, stats in family_stats.items(): + errors = stats.get("false_positives", 0) + stats.get("false_negatives", 0) + if errors: + failing.append({"family": family, "errors": errors, "pass_rate": stats.get("pass_rate")}) + failing.sort(key=lambda item: (-item["errors"], item["pass_rate"] or 0, item["family"])) + clean_count = sum( + 1 + for stats in family_stats.values() + if (stats.get("false_positives", 0) + stats.get("false_negatives", 0)) == 0 + ) + return { + "family_count": len(family_stats), + "clean_family_count": clean_count, + "failing_families": failing, + "weakest_family": { + "family": weakest[0], + "pass_rate": weakest[1].get("pass_rate"), + "errors": weakest[1].get("false_positives", 0) + weakest[1].get("false_negatives", 0), + } + if weakest + else None, + } + + +def summarize_calibration(report: dict | None, threshold: float | None) -> dict | None: + if not report or threshold is None: + return None + + positive_scores = [item["score"] for item in report["results"].get("should_trigger", [])] + should_not_scores = [item["score"] for item in report["results"].get("should_not_trigger", [])] + near_scores = [item["score"] for item in report["results"].get("near_neighbor", [])] + non_trigger_scores = should_not_scores + near_scores + total_cases = sum(len(items) for items in report["results"].values()) + boundary_cases = sum( + 1 + for items in report["results"].values() + for item in items + if item.get("boundary_case") + ) + + min_positive = min(positive_scores) if positive_scores else None + max_non_trigger = max(non_trigger_scores) if non_trigger_scores else None + positive_threshold_buffer = (min_positive - threshold) if min_positive is not None else None + negative_threshold_buffer = (threshold - max_non_trigger) if max_non_trigger is not None else None + score_gap = (min_positive - max_non_trigger) if min_positive is not None and max_non_trigger is not None else None + margin_candidates = [value for value in (positive_threshold_buffer, negative_threshold_buffer) if value is not None] + threshold_margin = min(margin_candidates) if margin_candidates else None + + risk_band = "healthy" + if report["false_positives"] or report["false_negatives"] or (score_gap is not None and score_gap < 0): + risk_band = "overlap" + elif threshold_margin is not None and threshold_margin < 0.03: + risk_band = "tight" + elif threshold_margin is not None and threshold_margin < 0.08: + risk_band = "watch" + elif total_cases and (boundary_cases / total_cases) > 0.25: + risk_band = "watch" + + return { + "threshold": safe_round(threshold), + "mean_positive_score": safe_round(sum(positive_scores) / len(positive_scores)) if positive_scores else None, + "mean_non_trigger_score": safe_round(sum(non_trigger_scores) / len(non_trigger_scores)) if non_trigger_scores else None, + "mean_near_neighbor_score": safe_round(sum(near_scores) / len(near_scores)) if near_scores else None, + "min_positive_score": safe_round(min_positive), + "max_non_trigger_score": safe_round(max_non_trigger), + "score_gap": safe_round(score_gap), + "threshold_margin": safe_round(threshold_margin), + "positive_threshold_buffer": safe_round(positive_threshold_buffer), + "negative_threshold_buffer": safe_round(negative_threshold_buffer), + "boundary_case_count": boundary_cases, + "boundary_case_rate": safe_round(boundary_cases / total_cases) if total_cases else None, + "risk_band": risk_band, + } + + +def build_gate_summary( + winner_report: dict | None, + current_report: dict | None, + baseline_report: dict | None, + threshold: float | None, +) -> dict: + return { + "winner": summarize_gate_report(winner_report), + "current": summarize_gate_report(current_report), + "baseline": summarize_gate_report(baseline_report), + "winner_calibration": summarize_calibration(winner_report, threshold), + "current_calibration": summarize_calibration(current_report, threshold), + "baseline_calibration": summarize_calibration(baseline_report, threshold), + "winner_family_health": summarize_family_health(winner_report), + "current_family_health": summarize_family_health(current_report), + "baseline_family_health": summarize_family_health(baseline_report), + } + + +def optimize( + current_description: str, + dev_cases: dict, + holdout_cases: dict | None, + config: dict, + baseline_description: str | None = None, + blind_holdout_cases: dict | None = None, + adversarial_cases: dict | None = None, +) -> dict: + dev_threshold = dev_cases.get("recommended_threshold", 0.48) + holdout_threshold = holdout_cases.get("recommended_threshold", dev_threshold) if holdout_cases else dev_threshold + blind_holdout_threshold = ( + blind_holdout_cases.get("recommended_threshold", holdout_threshold) if blind_holdout_cases else holdout_threshold + ) + adversarial_threshold = ( + adversarial_cases.get("recommended_threshold", blind_holdout_threshold) + if adversarial_cases + else blind_holdout_threshold + ) + + candidates = [] + for candidate in build_candidates(current_description, config): + dev_report = evaluate(candidate["description"], dev_cases, dev_threshold, config) + holdout_report = evaluate(candidate["description"], holdout_cases, holdout_threshold, config) if holdout_cases else None + candidates.append( + { + "candidate": summarize_candidate(candidate, dev_report, holdout_report), + "dev_report": dev_report, + "holdout_report": holdout_report, + } + ) + + candidates.sort(key=lambda item: item["candidate"]["selection_key"]) + winner = candidates[0] + current = next(item for item in candidates if item["candidate"]["id"] == "current") + + baseline = None + if baseline_description: + baseline_dev = evaluate(baseline_description, dev_cases, dev_threshold, config) + baseline_holdout = evaluate(baseline_description, holdout_cases, holdout_threshold, config) if holdout_cases else None + baseline = { + "description": sentence(baseline_description), + "estimated_tokens": estimate_tokens(sentence(baseline_description)), + "dev": baseline_dev, + "holdout": baseline_holdout, + } + + blind_reports = {} + if blind_holdout_cases: + blind_reports["current"] = evaluate(current["candidate"]["description"], blind_holdout_cases, blind_holdout_threshold, config) + blind_reports["winner"] = evaluate(winner["candidate"]["description"], blind_holdout_cases, blind_holdout_threshold, config) + if baseline: + blind_reports["baseline"] = evaluate( + baseline["description"], blind_holdout_cases, blind_holdout_threshold, config + ) + + judge_blind_reports = {} + if blind_holdout_cases: + judge_blind_reports["current"] = evaluate_judge(current["candidate"]["description"], blind_holdout_cases, config) + judge_blind_reports["winner"] = evaluate_judge(winner["candidate"]["description"], blind_holdout_cases, config) + if baseline: + judge_blind_reports["baseline"] = evaluate_judge(baseline["description"], blind_holdout_cases, config) + + adversarial_reports = {} + if adversarial_cases: + adversarial_reports["current"] = evaluate( + current["candidate"]["description"], adversarial_cases, adversarial_threshold, config + ) + adversarial_reports["winner"] = evaluate( + winner["candidate"]["description"], adversarial_cases, adversarial_threshold, config + ) + if baseline: + adversarial_reports["baseline"] = evaluate( + baseline["description"], adversarial_cases, adversarial_threshold, config + ) + + report = { + "current_description": sentence(current_description), + "current_candidate": current["candidate"], + "baseline": baseline, + "winner": winner["candidate"], + "winner_dev_report": winner["dev_report"], + "winner_holdout_report": winner["holdout_report"], + "current_dev_report": current["dev_report"], + "current_holdout_report": current["holdout_report"], + "winner_blind_holdout_report": blind_reports.get("winner"), + "current_blind_holdout_report": blind_reports.get("current"), + "baseline_blind_holdout_report": blind_reports.get("baseline"), + "winner_judge_blind_holdout_report": judge_blind_reports.get("winner"), + "current_judge_blind_holdout_report": judge_blind_reports.get("current"), + "baseline_judge_blind_holdout_report": judge_blind_reports.get("baseline"), + "winner_adversarial_holdout_report": adversarial_reports.get("winner"), + "current_adversarial_holdout_report": adversarial_reports.get("current"), + "baseline_adversarial_holdout_report": adversarial_reports.get("baseline"), + "candidates": [item["candidate"] for item in candidates], + "selection_logic": { + "priority": [ + "fewest false positives", + "fewest false negatives", + "highest near-neighbor pass rate", + "highest negative pass rate", + "highest precision", + "highest recall", + "shortest description", + ] + }, + "comparison": { + "winner_vs_current_dev": compare_reports(current["dev_report"], winner["dev_report"]), + "winner_vs_current_holdout": compare_reports(current["holdout_report"], winner["holdout_report"]) + if current["holdout_report"] and winner["holdout_report"] + else None, + "winner_vs_current_blind_holdout": compare_reports(blind_reports["current"], blind_reports["winner"]) + if blind_reports.get("current") and blind_reports.get("winner") + else None, + "winner_vs_baseline_dev": compare_reports(baseline["dev"], winner["dev_report"]) if baseline else None, + "winner_vs_baseline_holdout": compare_reports(baseline["holdout"], winner["holdout_report"]) + if baseline and baseline["holdout"] and winner["holdout_report"] + else None, + "winner_vs_baseline_blind_holdout": compare_reports(blind_reports["baseline"], blind_reports["winner"]) + if blind_reports.get("baseline") and blind_reports.get("winner") + else None, + "winner_vs_current_judge_blind_holdout": compare_reports( + judge_blind_reports["current"], judge_blind_reports["winner"] + ) + if judge_blind_reports.get("current") and judge_blind_reports.get("winner") + else None, + "winner_vs_baseline_judge_blind_holdout": compare_reports( + judge_blind_reports["baseline"], judge_blind_reports["winner"] + ) + if judge_blind_reports.get("baseline") and judge_blind_reports.get("winner") + else None, + "winner_vs_current_adversarial_holdout": compare_reports( + adversarial_reports["current"], adversarial_reports["winner"] + ) + if adversarial_reports.get("current") and adversarial_reports.get("winner") + else None, + "winner_vs_baseline_adversarial_holdout": compare_reports( + adversarial_reports["baseline"], adversarial_reports["winner"] + ) + if adversarial_reports.get("baseline") and adversarial_reports.get("winner") + else None, + }, + "acceptance_gates": { + "selection_basis": "dev only", + "holdout_non_regression": build_gate_summary( + winner["holdout_report"], + current["holdout_report"], + baseline["holdout"] if baseline else None, + holdout_threshold if holdout_cases else None, + ), + "blind_holdout_non_regression": build_gate_summary( + blind_reports.get("winner"), + blind_reports.get("current"), + blind_reports.get("baseline"), + blind_holdout_threshold if blind_holdout_cases else None, + ), + "judge_blind_holdout_non_regression": build_gate_summary( + judge_blind_reports.get("winner"), + judge_blind_reports.get("current"), + judge_blind_reports.get("baseline"), + None, + ), + "adversarial_holdout_non_regression": build_gate_summary( + adversarial_reports.get("winner"), + adversarial_reports.get("current"), + adversarial_reports.get("baseline"), + adversarial_threshold if adversarial_cases else None, + ), + }, + } + report["summary"] = { + "winner_label": report["winner"]["label"], + "winner_tokens": report["winner"]["estimated_tokens"], + "current_tokens": report["current_candidate"]["estimated_tokens"], + "winner_dev_total_errors": report["winner"]["dev"]["false_positives"] + report["winner"]["dev"]["false_negatives"], + "current_dev_total_errors": report["current_candidate"]["dev"]["false_positives"] + + report["current_candidate"]["dev"]["false_negatives"], + "winner_holdout_total_errors": report["winner"]["holdout"]["false_positives"] + report["winner"]["holdout"]["false_negatives"] + if report["winner"].get("holdout") + else None, + "current_holdout_total_errors": report["current_candidate"]["holdout"]["false_positives"] + + report["current_candidate"]["holdout"]["false_negatives"] + if report["current_candidate"].get("holdout") + else None, + "winner_blind_holdout_total_errors": sum(error_tuple(blind_reports.get("winner"))) + if blind_reports.get("winner") + else None, + "current_blind_holdout_total_errors": sum(error_tuple(blind_reports.get("current"))) + if blind_reports.get("current") + else None, + "winner_judge_blind_holdout_total_errors": sum(error_tuple(judge_blind_reports.get("winner"))) + if judge_blind_reports.get("winner") + else None, + "current_judge_blind_holdout_total_errors": sum(error_tuple(judge_blind_reports.get("current"))) + if judge_blind_reports.get("current") + else None, + "winner_adversarial_holdout_total_errors": sum(error_tuple(adversarial_reports.get("winner"))) + if adversarial_reports.get("winner") + else None, + "current_adversarial_holdout_total_errors": sum(error_tuple(adversarial_reports.get("current"))) + if adversarial_reports.get("current") + else None, + "winner_judge_blind_agreement_rate": ( + report["acceptance_gates"]["judge_blind_holdout_non_regression"]["winner"].get("judge_summary", {}).get("agreement_rate") + if report["acceptance_gates"]["judge_blind_holdout_non_regression"]["winner"] + else None + ), + "winner_adversarial_risk_band": report["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_calibration"]["risk_band"] + if report["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_calibration"] + else None, + "winner_adversarial_score_gap": report["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_calibration"]["score_gap"] + if report["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_calibration"] + else None, + "candidate_count": len(report["candidates"]), + } + if baseline: + report["summary"]["baseline_tokens"] = baseline["estimated_tokens"] + report["summary"]["baseline_dev_total_errors"] = baseline["dev"]["false_positives"] + baseline["dev"]["false_negatives"] + report["summary"]["baseline_holdout_total_errors"] = ( + baseline["holdout"]["false_positives"] + baseline["holdout"]["false_negatives"] + if baseline.get("holdout") + else None + ) + report["summary"]["baseline_blind_holdout_total_errors"] = ( + sum(error_tuple(blind_reports.get("baseline"))) if blind_reports.get("baseline") else None + ) + report["summary"]["baseline_judge_blind_holdout_total_errors"] = ( + sum(error_tuple(judge_blind_reports.get("baseline"))) if judge_blind_reports.get("baseline") else None + ) + report["summary"]["baseline_adversarial_holdout_total_errors"] = ( + sum(error_tuple(adversarial_reports.get("baseline"))) if adversarial_reports.get("baseline") else None + ) + return report + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate and score description candidates on dev, holdout, blind, and adversarial suites." + ) + parser.add_argument("--description-file", required=True) + parser.add_argument("--baseline-description-file") + parser.add_argument("--dev-cases", required=True) + parser.add_argument("--holdout-cases") + parser.add_argument("--blind-holdout-cases") + parser.add_argument("--adversarial-cases") + parser.add_argument("--semantic-config", required=True) + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--title", default="Description Optimization Report") + args = parser.parse_args() + + current_description = read_description(Path(args.description_file)) + baseline_description = read_description(Path(args.baseline_description_file)) if args.baseline_description_file else None + dev_cases = load_json(Path(args.dev_cases)) + holdout_cases = load_json(Path(args.holdout_cases)) if args.holdout_cases else None + blind_holdout_cases = load_json(Path(args.blind_holdout_cases)) if args.blind_holdout_cases else None + adversarial_cases = load_json(Path(args.adversarial_cases)) if args.adversarial_cases else None + config = load_semantic_config(Path(args.semantic_config)) + + report = optimize( + current_description, + dev_cases, + holdout_cases, + config, + baseline_description, + blind_holdout_cases, + adversarial_cases, + ) + rendered = json.dumps(report, ensure_ascii=False, indent=2) + if args.output_json: + Path(args.output_json).write_text(rendered + "\n", encoding="utf-8") + if args.output_md: + Path(args.output_md).write_text(render_markdown(report, args.title), encoding="utf-8") + print(rendered) + + +if __name__ == "__main__": + main() diff --git a/scripts/output_review_privacy.py b/scripts/output_review_privacy.py new file mode 100644 index 0000000..a19011c --- /dev/null +++ b/scripts/output_review_privacy.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by output-review import and world-class human evidence validators to reject raw content, credential, secret, token, and answer-key fields recursively." + +RAW_CONTENT_FIELDS = { + "api_key", + "assistant_message", + "assistant_messages", + "baseline_output", + "credential", + "credentials", + "input", + "inputs", + "message", + "messages", + "model_output", + "output", + "outputs", + "prompt", + "prompts", + "raw_content", + "raw_output", + "raw_prompt", + "raw_provider_prompt", + "raw_user_content", + "secret", + "secrets", + "token", + "transcript", + "transcripts", + "user_message", + "user_messages", + "with_skill_output", +} + +ANSWER_KEY_FIELDS = { + "answer_key", + "baseline_label", + "expected", + "expected_winner", + "expected_winner_role", + "expected_winner_variant", + "label", + "variant_label", + "with_skill_label", +} + +BLOCKED_DECISION_FIELDS = RAW_CONTENT_FIELDS | ANSWER_KEY_FIELDS + + +def forbidden_decision_field_paths(value: Any, prefix: str) -> list[str]: + found: list[str] = [] + if isinstance(value, dict): + for key, child in value.items(): + key_text = str(key).strip() + child_path = f"{prefix}.{key_text}" + if key_text.casefold() in BLOCKED_DECISION_FIELDS: + found.append(child_path) + found.extend(forbidden_decision_field_paths(child, child_path)) + elif isinstance(value, list): + for index, child in enumerate(value): + found.extend(forbidden_decision_field_paths(child, f"{prefix}[{index}]")) + return found + + +def forbidden_raw_content_field_paths(value: Any, prefix: str) -> list[str]: + found: list[str] = [] + if isinstance(value, dict): + for key, child in value.items(): + key_text = str(key).strip() + child_path = f"{prefix}.{key_text}" + if key_text.casefold() in RAW_CONTENT_FIELDS: + found.append(child_path) + found.extend(forbidden_raw_content_field_paths(child, child_path)) + elif isinstance(value, list): + for index, child in enumerate(value): + found.extend(forbidden_raw_content_field_paths(child, f"{prefix}[{index}]")) + return found diff --git a/scripts/prepare_output_review_kit.py b/scripts/prepare_output_review_kit.py new file mode 100644 index 0000000..26c972a --- /dev/null +++ b/scripts/prepare_output_review_kit.py @@ -0,0 +1,485 @@ +#!/usr/bin/env python3 +"""Prepare a reviewer-facing blind A/B output review kit.""" + +import argparse +import json +from pathlib import Path +from typing import Any + +from adjudicate_output_review import ( + build_decision_template, + confidence_value, + decision_index, + display_path, + load_json, + normalize_variant, +) +from html_rendering import html_text + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_BLIND_PACK_JSON = ROOT / "reports" / "output_blind_review_pack.json" +DEFAULT_BLIND_PACK_MD = ROOT / "reports" / "output_blind_review_pack.md" +DEFAULT_DECISIONS = ROOT / "reports" / "output_review_decisions.json" +DEFAULT_OUTPUT_JSON = ROOT / "reports" / "output_review_kit.json" +DEFAULT_OUTPUT_MD = ROOT / "reports" / "output_review_kit.md" +DEFAULT_OUTPUT_HTML = ROOT / "reports" / "output_review_kit.html" + + +def load_optional_decisions(path: Path) -> tuple[dict[str, Any], list[str]]: + if not path.exists(): + return {"schema_version": "1.0", "decisions": []}, [] + return load_json(path) + + +def pairs_from_pack(blind_pack: dict[str, Any]) -> list[dict[str, Any]]: + pairs = blind_pack.get("pairs", []) + if not isinstance(pairs, list): + return [] + return [item for item in pairs if isinstance(item, dict) and item.get("case_id")] + + +def decision_state(decision: dict[str, Any] | None) -> dict[str, Any]: + if not decision: + return { + "status": "awaiting-decision", + "winner_variant_recorded": False, + "confidence_recorded": False, + "reason_recorded": False, + "blocking_reason": "No reviewer choice has been recorded yet.", + } + winner = normalize_variant(decision.get("winner_variant", decision.get("reviewer_winner_variant", decision.get("winner", "")))) + confidence, confidence_failure = confidence_value(decision.get("confidence")) + reason = str(decision.get("reason", "")).strip() + if not winner and confidence is None and not reason: + return { + "status": "awaiting-decision", + "winner_variant_recorded": False, + "confidence_recorded": False, + "reason_recorded": False, + "blocking_reason": "Decision template exists but this row is still blank.", + } + failures = [] + if winner not in {"A", "B"}: + failures.append("winner_variant must be A or B") + if confidence_failure: + failures.append(confidence_failure) + if not reason: + failures.append("reason should explain the reviewer rationale") + if failures: + return { + "status": "needs-fix", + "winner_variant_recorded": winner in {"A", "B"}, + "confidence_recorded": confidence is not None, + "reason_recorded": bool(reason), + "blocking_reason": "; ".join(failures), + } + return { + "status": "ready-for-adjudication", + "winner_variant_recorded": True, + "confidence_recorded": confidence is not None, + "reason_recorded": True, + "blocking_reason": "Reviewer choice is complete enough for adjudication.", + } + + +def build_cases(blind_pack: dict[str, Any], decisions: dict[str, dict[str, Any]]) -> list[dict[str, Any]]: + cases = [] + for pair in pairs_from_pack(blind_pack): + case_id = str(pair.get("case_id", "")) + rubric = [] + for item in pair.get("rubric", []) if isinstance(pair.get("rubric", []), list) else []: + if isinstance(item, dict): + rubric.append( + { + "id": str(item.get("id", "")), + "description": str(item.get("description", "")), + "weight": float(item.get("weight", 1) or 0), + } + ) + cases.append( + { + "case_id": case_id, + "prompt": str(pair.get("prompt", "")), + "input_files": pair.get("input_files", []) if isinstance(pair.get("input_files", []), list) else [], + "review_instruction": str(pair.get("review_instruction", "")), + "rubric": rubric, + "variant_a": { + "blind_id": str(pair.get("variant_a", {}).get("blind_id", "")) if isinstance(pair.get("variant_a"), dict) else "", + "output": str(pair.get("variant_a", {}).get("output", "")) if isinstance(pair.get("variant_a"), dict) else "", + }, + "variant_b": { + "blind_id": str(pair.get("variant_b", {}).get("blind_id", "")) if isinstance(pair.get("variant_b"), dict) else "", + "output": str(pair.get("variant_b", {}).get("output", "")) if isinstance(pair.get("variant_b"), dict) else "", + }, + "decision_state": decision_state(decisions.get(case_id)), + } + ) + return cases + + +def build_summary(cases: list[dict[str, Any]], failures: list[str], reviewer: str, reviewed_at: str) -> dict[str, Any]: + status_counts: dict[str, int] = {} + for case in cases: + status = str(case.get("decision_state", {}).get("status", "awaiting-decision")) + status_counts[status] = status_counts.get(status, 0) + 1 + ready_count = status_counts.get("ready-for-adjudication", 0) + pending_count = status_counts.get("awaiting-decision", 0) + invalid_count = status_counts.get("needs-fix", 0) + return { + "case_count": len(cases), + "ready_for_adjudication_count": ready_count, + "pending_decision_count": pending_count, + "invalid_decision_count": invalid_count, + "reviewer_metadata_present": bool(str(reviewer).strip() and str(reviewed_at).strip()), + "answer_key_hidden": True, + "answer_key_path_exposed": False, + "ready_to_run_adjudication": bool(cases) and ready_count == len(cases) and invalid_count == 0, + "failure_count": len(failures), + } + + +def build_contract(blind_pack_md: Path, decisions_path: Path) -> dict[str, Any]: + return { + "reviewer_steps": [ + f"Open {display_path(blind_pack_md)} or this kit and compare Variant A vs Variant B for each case.", + f"Record choices in {display_path(decisions_path)} without opening the answer key.", + "Use winner_variant A or B, confidence from 0 to 1, and a short reason for every case.", + "Set reviewer_attestation truthfully after choices are recorded and before adjudication.", + "Run python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication after choices are recorded.", + "Refresh python3 scripts/yao.py review-studio . before asking for release approval.", + ], + "required_fields": { + "reviewer": "Human reviewer name or review group.", + "reviewed_at": "Review date or timestamp.", + "winner_variant": "A or B for every case.", + "confidence": "Optional numeric confidence from 0 to 1.", + "reason": "Required rationale based on the rubric, not on hidden labels.", + "reviewer_attestation": "Blind-review and answer-key-order attestation after decisions are complete.", + "review_integrity": "Blind pack and prompt fingerprints generated by the template.", + }, + "privacy_contract": [ + "The answer key is intentionally withheld from this kit.", + "Do not inspect hidden labels or expected winners before decisions are recorded.", + "Do not paste private user data into decision reasons.", + "Pending decisions must stay pending instead of being counted as human agreement.", + ], + } + + +def render_rubric(rubric: list[dict[str, Any]]) -> list[str]: + if not rubric: + return ["- No rubric items found."] + return [f"- `{item['id']}` ({item['weight']}): {item['description']}" for item in rubric] + + +def render_markdown(payload: dict[str, Any]) -> str: + summary = payload["summary"] + contract = payload["review_contract"] + lines = [ + "# Output Review Kit", + "", + "This reviewer-facing packet contains the blind A/B cases, decision fields, and command flow. It intentionally does not expose the answer key.", + "", + "## Summary", + "", + f"- cases: `{summary['case_count']}`", + f"- ready for adjudication: `{summary['ready_for_adjudication_count']}`", + f"- pending decisions: `{summary['pending_decision_count']}`", + f"- invalid decisions: `{summary['invalid_decision_count']}`", + f"- reviewer metadata present: `{str(summary['reviewer_metadata_present']).lower()}`", + f"- answer key hidden: `{str(summary['answer_key_hidden']).lower()}`", + f"- answer key path exposed: `{str(summary['answer_key_path_exposed']).lower()}`", + "", + "## Review Flow", + "", + ] + lines.extend(f"{index}. {step}" for index, step in enumerate(contract["reviewer_steps"], start=1)) + lines.extend(["", "## Required Fields", ""]) + lines.extend(f"- `{key}`: {value}" for key, value in contract["required_fields"].items()) + lines.extend(["", "## Privacy Contract", ""]) + lines.extend(f"- {item}" for item in contract["privacy_contract"]) + lines.extend( + [ + "", + "## Decision States", + "", + "| Case | State | Winner | Confidence | Reason | Blocking Reason |", + "| --- | --- | --- | --- | --- | --- |", + ] + ) + for case in payload["cases"]: + state = case["decision_state"] + lines.append( + f"| `{case['case_id']}` | `{state['status']}` | `{str(state['winner_variant_recorded']).lower()}` | " + f"`{str(state['confidence_recorded']).lower()}` | `{str(state['reason_recorded']).lower()}` | {state['blocking_reason']} |" + ) + lines.extend(["", "## Blind Cases", ""]) + for case in payload["cases"]: + lines.extend( + [ + f"### {case['case_id']}", + "", + f"Prompt: {case['prompt']}", + "", + "Rubric:", + *render_rubric(case["rubric"]), + "", + "#### Variant A", + "", + case["variant_a"]["output"] or "_No output found._", + "", + "#### Variant B", + "", + case["variant_b"]["output"] or "_No output found._", + "", + ] + ) + if payload.get("failures"): + lines.extend(["## Failures", ""]) + lines.extend(f"- {failure}" for failure in payload["failures"]) + return "\n".join(lines).strip() + "\n" + + +def status_label(status: str) -> str: + return { + "awaiting-decision": "Awaiting", + "needs-fix": "Needs fix", + "ready-for-adjudication": "Ready", + }.get(status, status) + + +def render_html_rubric(rubric: list[dict[str, Any]]) -> str: + if not rubric: + return "
  • No rubric items found.
  • " + return "".join( + "
  • {id}

    {description}

    {weight}
  • ".format( + id=html_text(item.get("id", "")), + description=html_text(item.get("description", "")), + weight=html_text(item.get("weight", "")), + ) + for item in rubric + ) + + +def render_html_cases(cases: list[dict[str, Any]]) -> str: + cards = [] + for index, case in enumerate(cases, start=1): + state = case.get("decision_state", {}) + status = str(state.get("status", "awaiting-decision")) + cards.append( + f""" +
    +
    +
    + Case {index:02d} +

    {html_text(case.get('case_id', ''))}

    +
    + {html_text(status_label(status))} +
    +

    {html_text(case.get('prompt', ''))}

    +

    Rubric

      {render_html_rubric(case.get('rubric', []))}
    +
    +

    Variant A

    {html_text(case.get('variant_a', {}).get('output', ''))}
    +

    Variant B

    {html_text(case.get('variant_b', {}).get('output', ''))}
    +
    +
    + Winner recorded: {html_text(str(state.get('winner_variant_recorded', False)).lower())} + Confidence: {html_text(str(state.get('confidence_recorded', False)).lower())} + Reason: {html_text(str(state.get('reason_recorded', False)).lower())} + {html_text(state.get('blocking_reason', ''))} +
    +
    """ + ) + return "\n".join(cards) + + +def decision_template_json(cases: list[dict[str, Any]]) -> str: + template = { + "schema_version": "1.0", + "reviewer": "", + "reviewed_at": "", + "decisions": [ + {"case_id": case.get("case_id", ""), "winner_variant": "", "confidence": None, "reason": ""} + for case in cases + ], + } + return json.dumps(template, ensure_ascii=False, indent=2) + + +def render_html(payload: dict[str, Any]) -> str: + summary = payload["summary"] + contract = payload["review_contract"] + stats = [ + ("Cases", summary["case_count"]), + ("Ready", summary["ready_for_adjudication_count"]), + ("Pending", summary["pending_decision_count"]), + ("Invalid", summary["invalid_decision_count"]), + ] + stat_html = "".join(f"
    {html_text(label)}{html_text(value)}
    " for label, value in stats) + flow_html = "".join(f"
  • {html_text(step)}
  • " for step in contract["reviewer_steps"]) + privacy_html = "".join(f"
  • {html_text(item)}
  • " for item in contract["privacy_contract"]) + decision_json = html_text(decision_template_json(payload["cases"])) + return f""" + + + + + Output Review Kit + + + + +
    +
    + Blind A/B Human Review +

    Reviewer cockpit for output quality decisions

    +

    Compare visible Variant A and Variant B outputs, fill the decision file, then run adjudication. The answer key is intentionally hidden from this page.

    +
    {stat_html}
    +
    +
    +

    Review Flow

      {flow_html}
    + +
    +
    {render_html_cases(payload["cases"])}
    +

    Decision Template

    Use this shape in {html_text(payload['artifacts']['decisions'])}; leave a case blank when the reviewer is not ready.

    {decision_json}
    +
    + + +""" + + +def prepare_output_review_kit( + blind_pack_json: Path, + blind_pack_md: Path, + decisions_path: Path, + output_json: Path, + output_md: Path, + output_html: Path | None = None, + write_template: bool = False, +) -> dict[str, Any]: + blind_pack, failures = load_json(blind_pack_json) + decisions_payload, decision_failures = load_optional_decisions(decisions_path) + failures.extend(decision_failures) + template_written = False + if write_template and blind_pack and not decisions_path.exists(): + decisions_path.parent.mkdir(parents=True, exist_ok=True) + decisions_path.write_text( + json.dumps(build_decision_template(blind_pack), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + template_written = True + decisions_payload, decision_failures = load_optional_decisions(decisions_path) + failures.extend(decision_failures) + decisions, index_failures = decision_index(decisions_payload) + failures.extend(index_failures) + case_ids = {str(pair.get("case_id", "")) for pair in pairs_from_pack(blind_pack)} + for case_id in decisions: + if case_id not in case_ids: + failures.append(f"decision references unknown case_id: {case_id}") + cases = build_cases(blind_pack, decisions) + summary = build_summary(cases, failures, str(decisions_payload.get("reviewer", "")), str(decisions_payload.get("reviewed_at", ""))) + payload = { + "schema_version": "1.0", + "ok": not failures, + "summary": summary, + "artifacts": { + "reviewer_kit_json": display_path(output_json), + "reviewer_kit_markdown": display_path(output_md), + "reviewer_kit_html": display_path(output_html) if output_html else "", + "blind_pack_json": display_path(blind_pack_json), + "blind_pack_markdown": display_path(blind_pack_md), + "decisions": display_path(decisions_path), + "adjudication_json": "reports/output_review_adjudication.json", + "adjudication_markdown": "reports/output_review_adjudication.md", + }, + "template_written": template_written, + "review_contract": build_contract(blind_pack_md, decisions_path), + "cases": cases, + "failures": failures, + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + if output_html: + output_html.parent.mkdir(parents=True, exist_ok=True) + output_html.write_text(render_html(payload), encoding="utf-8") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Prepare a reviewer-facing blind A/B output review kit.") + parser.add_argument("--blind-pack-json", default=str(DEFAULT_BLIND_PACK_JSON)) + parser.add_argument("--blind-pack-md", default=str(DEFAULT_BLIND_PACK_MD)) + parser.add_argument("--decisions", default=str(DEFAULT_DECISIONS)) + parser.add_argument("--output-json", default=str(DEFAULT_OUTPUT_JSON)) + parser.add_argument("--output-md", default=str(DEFAULT_OUTPUT_MD)) + parser.add_argument("--output-html", default=str(DEFAULT_OUTPUT_HTML)) + parser.add_argument("--write-template", action="store_true") + args = parser.parse_args() + + payload = prepare_output_review_kit( + Path(args.blind_pack_json).resolve(), + Path(args.blind_pack_md).resolve(), + Path(args.decisions).resolve(), + Path(args.output_json).resolve(), + Path(args.output_md).resolve(), + Path(args.output_html).resolve() if args.output_html else None, + write_template=args.write_template, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/prepare_world_class_submission_kit.py b/scripts/prepare_world_class_submission_kit.py new file mode 100644 index 0000000..66c3412 --- /dev/null +++ b/scripts/prepare_world_class_submission_kit.py @@ -0,0 +1,560 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import shlex +from datetime import date +from pathlib import Path +from typing import Any + +from render_world_class_evidence_intake import build_intake +from world_class_evidence_contract import DISALLOWED_REAL_ARTIFACTS +from world_class_phase_queue import build_phase_queue, summarize_phase_queue +from world_class_repair_checklist import build_repair_checklist, summarize_repair_checklist +from world_class_source_checks import build_source_checklist, summarize_source_checklist +from world_class_submission_matrix import build_evidence_matrix, summarize_evidence_matrix +from world_class_submission_kit_rendering import render_html, render_readme + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Prepares editable world-class evidence intake packets without counting drafts as accepted evidence." + + +def rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def shell_path(path: Path, root: Path) -> str: + return shlex.quote(rel_path(path, root)) + + +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 has_glob_pattern(value: str) -> bool: + return any(token in value for token in ("*", "?", "[")) + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def requested_checklist_items(intake: dict[str, Any], evidence_keys: list[str]) -> list[dict[str, Any]]: + items = intake.get("operator_checklist", []) + if not evidence_keys: + return items + requested = set(evidence_keys) + return [item for item in items if item.get("evidence_key") in requested] + + +def template_result_by_key(intake: dict[str, Any]) -> dict[str, dict[str, Any]]: + return {str(item.get("evidence_key")): item for item in intake.get("templates", [])} + + +def artifact_rows_by_key_and_path(rows: list[dict[str, Any]]) -> dict[tuple[str, str], dict[str, Any]]: + ready: dict[tuple[str, str], dict[str, Any]] = {} + for row in rows: + key = str(row.get("evidence_key", "")) + path = str(row.get("path", "")) + if key and path and row.get("artifact_ref_ready"): + ready[(key, path)] = row + return ready + + +def template_artifact_ref_paths(skill_dir: Path, item: dict[str, Any]) -> set[str]: + template_path = skill_dir / str(item.get("template_path", "")) + errors: list[str] = [] + payload = load_template_payload(template_path, errors) + if payload is None: + return set() + refs = payload.get("artifact_refs", []) + if not isinstance(refs, list): + return set() + paths: set[str] = set() + for ref in refs: + if not isinstance(ref, dict): + continue + path = str(ref.get("path", "")).strip() + if path: + paths.add(path) + return paths + + +def load_template_payload(path: Path, errors: list[str]) -> dict[str, Any] | None: + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + errors.append("template file is not valid JSON") + return None + return payload if isinstance(payload, dict) else None + + +def prefill_artifact_refs( + payload: dict[str, Any], + evidence_key: str, + ready_rows: dict[tuple[str, str], dict[str, Any]], +) -> dict[str, int]: + refs = payload.get("artifact_refs", []) + stats = { + "prefilled_artifact_ref_count": 0, + "unfilled_artifact_ref_count": 0, + } + if not isinstance(refs, list): + return stats + for ref in refs: + if not isinstance(ref, dict): + stats["unfilled_artifact_ref_count"] += 1 + continue + path = str(ref.get("path", "")).strip() + row = ready_rows.get((evidence_key, path)) + if row and row.get("sha256"): + ref["sha256"] = row["sha256"] + ref["contains_raw_content"] = False + stats["prefilled_artifact_ref_count"] += 1 + else: + stats["unfilled_artifact_ref_count"] += 1 + return stats + + +def copy_template( + skill_dir: Path, + output_dir: Path, + item: dict[str, Any], + template_results: dict[str, dict[str, Any]], + ready_rows: dict[tuple[str, str], dict[str, Any]], + overwrite: bool, + prefill_artifacts: bool, +) -> dict[str, Any]: + key = str(item.get("evidence_key", "")) + template_result = template_results.get(key, {}) + template_path = skill_dir / str(item.get("template_path", "")) + output_path = output_dir / f"{key}.json" + errors: list[str] = [] + + if template_result.get("status") != "pass": + errors.append("template failed intake validation") + if not template_path.exists(): + errors.append("template file is missing") + + if errors: + return { + "evidence_key": key, + "status": "skipped", + "template_path": rel_path(template_path, skill_dir), + "output_path": rel_path(output_path, skill_dir), + "prefilled_artifact_ref_count": 0, + "unfilled_artifact_ref_count": 0, + "errors": errors, + } + + if output_path.exists() and not overwrite: + return { + "evidence_key": key, + "status": "exists", + "template_path": rel_path(template_path, skill_dir), + "output_path": rel_path(output_path, skill_dir), + "prefilled_artifact_ref_count": 0, + "unfilled_artifact_ref_count": 0, + "errors": [], + } + + payload = load_template_payload(template_path, errors) + if errors or payload is None: + return { + "evidence_key": key, + "status": "skipped", + "template_path": rel_path(template_path, skill_dir), + "output_path": rel_path(output_path, skill_dir), + "prefilled_artifact_ref_count": 0, + "unfilled_artifact_ref_count": 0, + "errors": errors or ["template file is not a JSON object"], + } + + prefill_stats = ( + prefill_artifact_refs(payload, key, ready_rows) + if prefill_artifacts + else {"prefilled_artifact_ref_count": 0, "unfilled_artifact_ref_count": 0} + ) + output_path.parent.mkdir(parents=True, exist_ok=True) + write_json(output_path, payload) + return { + "evidence_key": key, + "status": "written", + "template_path": rel_path(template_path, skill_dir), + "output_path": rel_path(output_path, skill_dir), + **prefill_stats, + "errors": [], + } + + +def artifact_row(skill_dir: Path, evidence_key: str, pattern: str, path: Path, status: str) -> dict[str, Any]: + exists = path.exists() + is_file = exists and path.is_file() + relative = rel_path(path, skill_dir) if exists else pattern + contains_raw_content = relative in DISALLOWED_REAL_ARTIFACTS + digest = sha256_file(path) if is_file and not contains_raw_content else "" + row_status = status if status else ("ready" if is_file else "missing") + if contains_raw_content: + row_status = "raw-content-disallowed" + return { + "evidence_key": evidence_key, + "source_pattern": pattern, + "path": relative, + "status": row_status, + "exists": exists, + "is_file": is_file, + "sha256": digest, + "artifact_ref_ready": bool(is_file and digest and not contains_raw_content), + "copy_path": relative if is_file else "", + "copy_sha256": digest, + "contains_raw_content": contains_raw_content, + "concrete_reference_required": has_glob_pattern(pattern), + } + + +def with_artifact_role(row: dict[str, Any], submission_ref_paths: set[str]) -> dict[str, Any]: + is_submission_ref = str(row.get("path", "")) in submission_ref_paths + return { + **row, + "artifact_role": "submission-ref" if is_submission_ref else "supporting-evidence", + "submission_ref_required": is_submission_ref, + } + + +def artifact_checklist_for_item(skill_dir: Path, item: dict[str, Any]) -> list[dict[str, Any]]: + key = str(item.get("evidence_key", "")) + must_collect = item.get("must_collect", {}) if isinstance(item.get("must_collect", {}), dict) else {} + artifacts = must_collect.get("evidence_artifacts", []) + if not isinstance(artifacts, list): + return [] + submission_ref_paths = template_artifact_ref_paths(skill_dir, item) + rows: list[dict[str, Any]] = [] + for artifact in artifacts: + pattern = str(artifact or "").strip() + if not pattern: + continue + if Path(pattern).is_absolute() or ".." in Path(pattern).parts: + rows.append( + with_artifact_role( + { + "evidence_key": key, + "source_pattern": pattern, + "path": pattern, + "status": "unsafe-path", + "exists": False, + "is_file": False, + "sha256": "", + "artifact_ref_ready": False, + "copy_path": "", + "copy_sha256": "", + "contains_raw_content": False, + "concrete_reference_required": True, + }, + submission_ref_paths, + ) + ) + continue + if has_glob_pattern(pattern): + matches = sorted(path for path in skill_dir.glob(pattern) if path.is_file()) + if not matches: + rows.append( + with_artifact_role( + { + "evidence_key": key, + "source_pattern": pattern, + "path": pattern, + "status": "glob-no-match", + "exists": False, + "is_file": False, + "sha256": "", + "artifact_ref_ready": False, + "copy_path": "", + "copy_sha256": "", + "contains_raw_content": False, + "concrete_reference_required": True, + }, + submission_ref_paths, + ) + ) + continue + for match in matches: + rows.append(with_artifact_role(artifact_row(skill_dir, key, pattern, match, "ready"), submission_ref_paths)) + continue + rows.append(with_artifact_role(artifact_row(skill_dir, key, pattern, skill_dir / pattern, ""), submission_ref_paths)) + return rows + + +def build_artifact_checklist(skill_dir: Path, items: list[dict[str, Any]]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + for item in items: + rows.extend(artifact_checklist_for_item(skill_dir, item)) + return rows + + +def build_handoff_steps( + commands: dict[str, str], + *, + files: list[dict[str, Any]], + source_blocked_count: int, + invalid_draft_count: int, +) -> list[dict[str, Any]]: + drafts_ready = bool(files) and invalid_draft_count == 0 + return [ + { + "step_id": "prepare-drafts", + "label": "Prepare editable drafts", + "status": "ready" if drafts_ready else "fix-required", + "command": commands["prepare_submission"], + "completion_signal": "JSON drafts, submission_manifest.json, README.md, and index.html are present.", + "counts_as_completion": False, + "blocking_condition": "One or more drafts were skipped or failed template validation." if invalid_draft_count else "", + }, + { + "step_id": "collect-source", + "label": "Collect real external or human evidence", + "status": "blocked" if source_blocked_count else "ready", + "command": "", + "completion_signal": "Source aggregate reports satisfy the required provider, human, native, or telemetry checks.", + "counts_as_completion": False, + "blocking_condition": ( + f"{source_blocked_count} source check(s) still block ledger review." + if source_blocked_count + else "" + ), + }, + { + "step_id": "edit-submission", + "label": "Edit submission packet", + "status": "manual", + "command": "", + "completion_signal": "template_only is false only after real evidence exists; ledger_reviewer_approved, ledger_reviewer, and ledger_reviewed_at stay unset until reviewer approval.", + "counts_as_completion": False, + "blocking_condition": "Raw prompts, outputs, transcripts, credentials, or private content must not be included.", + }, + { + "step_id": "validate-intake", + "label": "Validate intake contract", + "status": "pending", + "command": commands["validate_intake"], + "completion_signal": "world_class_evidence_intake reports valid submissions and no invalid packets before reviewer approval.", + "counts_as_completion": False, + "blocking_condition": "A valid packet is ready for ledger review but is not accepted evidence by itself.", + }, + { + "step_id": "review-submission", + "label": "Review submission queue", + "status": "pending", + "command": commands["review_submission"], + "completion_signal": "world_class_submission_review shows ready-for-ledger-review before reviewer identity, timestamp, and approval are set.", + "counts_as_completion": False, + "blocking_condition": "Reviewer queue output is advisory and cannot accept evidence.", + }, + { + "step_id": "refresh-ledger", + "label": "Refresh evidence ledger", + "status": "pending", + "command": commands["refresh_ledger"], + "completion_signal": "world_class_evidence_ledger accepts the evidence entry with valid source checks.", + "counts_as_completion": False, + "blocking_condition": "Ledger remains the only world-class readiness source of truth.", + }, + { + "step_id": "guard-claim", + "label": "Guard public claim", + "status": "pending", + "command": commands["guard_claim"], + "completion_signal": "world_class_claim_guard allows the public readiness claim.", + "counts_as_completion": False, + "blocking_condition": "Public world-class claims stay blocked until every ledger entry is accepted.", + }, + ] + + +def build_submission_kit( + skill_dir: Path, + output_dir: Path, + generated_at: str, + evidence_keys: list[str] | None = None, + overwrite: bool = False, + prefill_artifacts: bool = False, + output_html: Path | None = None, +) -> dict[str, Any]: + intake = build_intake(skill_dir, generated_at, submissions_dir=output_dir) + items = requested_checklist_items(intake, evidence_keys or []) + valid_keys = {str(item.get("evidence_key")) for item in intake.get("operator_checklist", [])} + unknown_keys = sorted(set(evidence_keys or []) - valid_keys) + template_results = template_result_by_key(intake) + artifact_checklist = build_artifact_checklist(skill_dir, items) + ready_rows = artifact_rows_by_key_and_path(artifact_checklist) + files = [ + copy_template( + skill_dir, + output_dir, + item, + template_results, + ready_rows, + overwrite, + prefill_artifacts, + ) + for item in items + ] + source_checklist = build_source_checklist(items) + evidence_matrix = build_evidence_matrix(items, files, artifact_checklist, source_checklist) + repair_checklist = build_repair_checklist(items, files, artifact_checklist, source_checklist, unknown_keys) + phase_queue = build_phase_queue(repair_checklist) + manifest_path = output_dir / "submission_manifest.json" + readme_path = output_dir / "README.md" + output_html = output_html or (output_dir / "index.html") + written_count = sum(1 for item in files if item["status"] == "written") + existing_count = sum(1 for item in files if item["status"] == "exists") + skipped_count = sum(1 for item in files if item["status"] == "skipped") + prefilled_artifact_ref_count = sum(item.get("prefilled_artifact_ref_count", 0) for item in files) + unfilled_artifact_ref_count = sum(item.get("unfilled_artifact_ref_count", 0) for item in files) + artifact_ready_count = sum(1 for item in artifact_checklist if item.get("artifact_ref_ready")) + artifact_missing_count = sum(1 for item in artifact_checklist if not item.get("artifact_ref_ready")) + artifact_glob_count = sum(1 for item in artifact_checklist if item.get("concrete_reference_required")) + submission_ref_rows = [item for item in artifact_checklist if item.get("submission_ref_required")] + supporting_rows = [item for item in artifact_checklist if not item.get("submission_ref_required")] + submission_ref_ready_count = sum(1 for item in submission_ref_rows if item.get("artifact_ref_ready")) + supporting_artifact_ready_count = sum(1 for item in supporting_rows if item.get("artifact_ref_ready")) + source_summary = summarize_source_checklist(source_checklist) + matrix_summary = summarize_evidence_matrix(evidence_matrix) + repair_summary = summarize_repair_checklist(repair_checklist) + phase_queue_summary = summarize_phase_queue(phase_queue) + ok = not unknown_keys and skipped_count == 0 + output_dir_arg = shell_path(output_dir, skill_dir) + requested_key_args = " ".join(f"--evidence-key {shlex.quote(key)}" for key in (evidence_keys or [])) + prepare_command = f"python3 scripts/yao.py world-class-submission-kit . --output-dir {output_dir_arg}" + if requested_key_args: + prepare_command = f"{prepare_command} {requested_key_args}" + if prefill_artifacts: + prepare_command = f"{prepare_command} --prefill-artifacts" + commands = { + "prepare_submission": prepare_command, + "validate_intake": f"python3 scripts/yao.py world-class-intake . --submissions-dir {output_dir_arg}", + "review_submission": f"python3 scripts/yao.py world-class-submission-review . --submissions-dir {output_dir_arg}", + "refresh_ledger": f"python3 scripts/yao.py world-class-ledger . --submissions-dir {output_dir_arg}", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard .", + } + handoff_steps = build_handoff_steps( + commands, + files=files, + source_blocked_count=source_summary["source_blocked_count"], + invalid_draft_count=skipped_count + len(unknown_keys), + ) + report = { + "schema_version": "1.0", + "ok": ok, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "output_dir": rel_path(output_dir, skill_dir), + "summary": { + "requested_count": len(items) + len(unknown_keys), + "prepared_count": len(files), + "written_count": written_count, + "existing_count": existing_count, + "skipped_count": skipped_count, + "unknown_key_count": len(unknown_keys), + "artifact_checklist_count": len(artifact_checklist), + "artifact_ready_count": artifact_ready_count, + "artifact_missing_count": artifact_missing_count, + "artifact_glob_expansion_count": artifact_glob_count, + "submission_ref_count": len(submission_ref_rows), + "submission_ref_ready_count": submission_ref_ready_count, + "submission_ref_missing_count": len(submission_ref_rows) - submission_ref_ready_count, + "supporting_artifact_count": len(supporting_rows), + "supporting_artifact_ready_count": supporting_artifact_ready_count, + "supporting_artifact_missing_count": len(supporting_rows) - supporting_artifact_ready_count, + "artifact_prefill_enabled": prefill_artifacts, + "artifact_ref_prefill_count": prefilled_artifact_ref_count, + "artifact_ref_unfilled_count": unfilled_artifact_ref_count, + **source_summary, + **matrix_summary, + **repair_summary, + **phase_queue_summary, + "handoff_step_count": len(handoff_steps), + "handoff_blocked_count": sum(1 for item in handoff_steps if item["status"] == "blocked"), + "handoff_fix_required_count": sum(1 for item in handoff_steps if item["status"] == "fix-required"), + "handoff_counts_as_completion": False, + "drafts_count_as_evidence": False, + "ledger_counts_submission_as_completion": False, + "decision": "submission-kit-ready" if ok else "fix-submission-kit", + }, + "unknown_evidence_keys": unknown_keys, + "files": files, + "artifact_checklist": artifact_checklist, + "source_checklist": source_checklist, + "evidence_matrix": evidence_matrix, + "repair_checklist": repair_checklist, + "phase_queue": phase_queue, + "operator_handoff": handoff_steps, + "evidence_items": items, + "commands": commands, + "safety": { + "template_only_drafts": True, + "artifact_prefill_counts_as_evidence": False, + "operator_handoff_counts_as_evidence": False, + "real_evidence_required_before_template_only_false": True, + "raw_content_allowed": False, + "credentials_allowed": False, + "drafts_count_as_evidence": False, + }, + "artifacts": { + "manifest": rel_path(manifest_path, skill_dir), + "readme": rel_path(readme_path, skill_dir), + "html": rel_path(output_html, skill_dir), + }, + } + output_dir.mkdir(parents=True, exist_ok=True) + write_json(manifest_path, report) + readme_path.write_text(render_readme(report), encoding="utf-8") + output_html.parent.mkdir(parents=True, exist_ok=True) + output_html.write_text(render_html(report), encoding="utf-8") + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Prepare editable world-class evidence submission drafts.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-dir", default="evidence/world_class/submission-kit") + parser.add_argument("--evidence-key", action="append", default=[]) + parser.add_argument("--overwrite", action="store_true") + parser.add_argument( + "--prefill-artifacts", + action="store_true", + help="Insert SHA-256 digests for currently available aggregate artifacts while keeping drafts template-only.", + ) + parser.add_argument("--generated-at", default=date.today().isoformat()) + parser.add_argument("--output-html") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + output_dir = Path(args.output_dir) + if not output_dir.is_absolute(): + output_dir = skill_dir / output_dir + report = build_submission_kit( + skill_dir, + output_dir.resolve(), + args.generated_at, + evidence_keys=args.evidence_key, + overwrite=args.overwrite, + prefill_artifacts=args.prefill_artifacts, + output_html=Path(args.output_html).resolve() if args.output_html else None, + ) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/probe_runtime_permissions.py b/scripts/probe_runtime_permissions.py new file mode 100644 index 0000000..6a1ddbd --- /dev/null +++ b/scripts/probe_runtime_permissions.py @@ -0,0 +1,454 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +DEFAULT_TARGETS = ["openai", "claude", "generic", "vscode"] + + +def display_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +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 {} + try: + payload = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + except yaml.YAMLError: + return {} + return payload if isinstance(payload, dict) else {} + + +def expected_capabilities(skill_dir: Path) -> list[str]: + trust = load_json(skill_dir / "reports" / "security_trust_report.json") + governance = trust.get("permission_governance", {}) if isinstance(trust.get("permission_governance"), dict) else {} + required = governance.get("required_capabilities", []) + if isinstance(required, list) and required: + return sorted(str(item) for item in required) + summary = trust.get("summary", {}) if isinstance(trust.get("summary"), dict) else {} + candidates = [] + if int(summary.get("network_script_count", 0) or 0): + candidates.append("network") + if int(summary.get("file_write_script_count", 0) or 0): + candidates.append("file_write") + if any(item.get("uses_subprocess") for item in trust.get("scripts", []) if isinstance(item, dict)): + candidates.append("subprocess") + if int(summary.get("interactive_script_count", 0) or 0): + candidates.append("interactive") + return sorted(candidates) + + +def add_check(checks: list[dict[str, Any]], failures: list[str], key: str, condition: bool, detail: str) -> None: + checks.append({"key": key, "passed": condition, "detail": detail}) + if not condition: + failures.append(detail) + + +def sorted_strings(value: Any) -> list[str]: + return sorted(str(item) for item in value) if isinstance(value, list) else [] + + +def check_index(checks: list[Any]) -> dict[str, str]: + indexed: dict[str, str] = {} + for item in checks: + if not isinstance(item, dict): + continue + check_id = str(item.get("id") or item.get("key") or "").strip() + status = str(item.get("status") or ("pass" if item.get("passed") is True else "")).strip() + if check_id: + indexed[check_id] = status + return indexed + + +def installer_enforcement_source( + skill_dir: Path, + package_dir: Path, + targets: list[str], + expected: list[str], + install_simulation_path: Path | None, +) -> dict[str, Any]: + path = install_simulation_path or skill_dir / "reports" / "install_simulation.json" + report = load_json(path) + source_display = display_path(path, skill_dir) + if not path.exists() or not report: + return { + "source": source_display, + "source_status": "missing", + "package_dir_matches": False, + "summary": {}, + "targets": { + target: { + "target": target, + "source_status": "missing", + "enforced": False, + "enforced_capabilities": [], + "missing_capabilities": expected, + "failure_details": ["install simulation report is missing"], + } + for target in targets + }, + } + + expected_package_dir = display_path(package_dir, skill_dir) + observed_package_dir = str(report.get("package_dir", "")).strip() + package_matches = observed_package_dir in {expected_package_dir, str(package_dir)} + summary = report.get("summary", {}) if isinstance(report.get("summary"), dict) else {} + if not package_matches: + source_status = "package-mismatch" + else: + source_status = "present" + + indexed = check_index(report.get("checks", []) if isinstance(report.get("checks"), list) else []) + target_results: dict[str, Any] = {} + for target in targets: + enforced_capabilities = [] + missing_capabilities = [] + failure_details = [] + for capability in expected: + approval_id = f"permission-{target}-{capability}-approved" + enforcement_id = f"permission-{target}-{capability}-target-enforcement" + approval_ok = indexed.get(approval_id) == "pass" + enforcement_ok = indexed.get(enforcement_id) == "pass" + if source_status == "present" and approval_ok and enforcement_ok: + enforced_capabilities.append(capability) + else: + missing_capabilities.append(capability) + if source_status == "present" and not approval_ok: + failure_details.append(f"{approval_id} did not pass") + if source_status == "present" and not enforcement_ok: + failure_details.append(f"{enforcement_id} did not pass") + if source_status == "package-mismatch": + failure_details.append( + f"install simulation package_dir {observed_package_dir or 'missing'} does not match probed package_dir {expected_package_dir}" + ) + target_results[target] = { + "target": target, + "source_status": source_status, + "enforced": bool(expected) and source_status == "present" and not missing_capabilities, + "enforced_capabilities": enforced_capabilities, + "missing_capabilities": missing_capabilities, + "failure_details": failure_details, + } + + return { + "source": source_display, + "source_status": source_status, + "package_dir_matches": package_matches, + "summary": { + "installer_permission_enforced_count": int(summary.get("installer_permission_enforced_count", 0) or 0), + "installer_permission_failure_count": int(summary.get("installer_permission_failure_count", 0) or 0), + "permission_target_count": int(summary.get("permission_target_count", 0) or 0), + "permission_capability_count": int(summary.get("permission_capability_count", 0) or 0), + "failure_count": int(summary.get("failure_count", 0) or 0), + }, + "targets": target_results, + } + + +def probe_openai_yaml(package_dir: Path, expected: list[str]) -> tuple[list[dict[str, Any]], list[str]]: + checks: list[dict[str, Any]] = [] + failures: list[str] = [] + path = package_dir / "targets" / "openai" / "agents" / "openai.yaml" + payload = load_yaml(path) + permission_contract = payload.get("compatibility", {}).get("permission_contract", {}) if payload else {} + add_check(checks, failures, "openai-yaml-present", bool(payload), "OpenAI permission metadata YAML is readable") + add_check( + checks, + failures, + "openai-yaml-permissions", + sorted_strings(permission_contract.get("declared_capabilities")) == expected, + "OpenAI YAML permission contract mirrors expected capabilities", + ) + add_check( + checks, + failures, + "openai-yaml-native-flag", + isinstance(permission_contract.get("native_enforcement"), bool), + "OpenAI YAML declares native_enforcement as a boolean", + ) + return checks, failures + + +def probe_target( + skill_dir: Path, + package_dir: Path, + target: str, + expected: list[str], + installer_targets: dict[str, Any], +) -> dict[str, Any]: + adapter_path = package_dir / "targets" / target / "adapter.json" + checks: list[dict[str, Any]] = [] + failures: list[str] = [] + adapter = load_json(adapter_path) + add_check(checks, failures, "adapter-present", bool(adapter), f"{target} adapter.json is readable") + + permission_contract = adapter.get("permission_contract", {}) if adapter else {} + target_contract = adapter.get("target_permission_contract", {}) if adapter else {} + add_check(checks, failures, "permission-contract-present", bool(permission_contract), f"{target} adapter includes permission_contract") + add_check(checks, failures, "target-contract-present", bool(target_contract), f"{target} adapter includes target_permission_contract") + add_check( + checks, + failures, + "source-available", + permission_contract.get("source_available") is True, + f"{target} permission_contract links to an available trust report", + ) + add_check( + checks, + failures, + "declared-capabilities-match", + sorted_strings(target_contract.get("declared_capabilities")) == expected, + f"{target} target_permission_contract mirrors expected capabilities", + ) + add_check( + checks, + failures, + "capability-counts-present", + isinstance(target_contract.get("capability_counts"), dict), + f"{target} target_permission_contract includes capability_counts", + ) + add_check( + checks, + failures, + "native-enforcement-boolean", + isinstance(target_contract.get("native_enforcement"), bool), + f"{target} target_permission_contract declares native_enforcement as a boolean", + ) + add_check( + checks, + failures, + "representation-present", + bool(str(target_contract.get("representation", "")).strip()), + f"{target} target_permission_contract declares where permission metadata is represented", + ) + add_check( + checks, + failures, + "operator-note-present", + bool(str(target_contract.get("operator_note", "")).strip()), + f"{target} target_permission_contract includes an operator_note", + ) + add_check( + checks, + failures, + "review-required-matches", + bool(target_contract.get("review_required")) == bool(expected), + f"{target} review_required matches whether capabilities are required", + ) + + yaml_checks: list[dict[str, Any]] = [] + yaml_failures: list[str] = [] + if target == "openai": + yaml_checks, yaml_failures = probe_openai_yaml(package_dir, expected) + checks.extend(yaml_checks) + failures.extend(yaml_failures) + + native = target_contract.get("native_enforcement") + metadata_fallback = native is False and bool(target_contract.get("representation")) and bool(target_contract.get("operator_note")) + assurance = "native-enforced" if native is True else ("metadata-fallback-explicit" if metadata_fallback else "missing") + residual_risks = [] + if native is False: + residual_risks.append("Client-native permission enforcement is not provided by this target; installer or operator must honor metadata.") + installer_enforcement = installer_targets.get( + target, + { + "target": target, + "source_status": "missing", + "enforced": False, + "enforced_capabilities": [], + "missing_capabilities": expected, + "failure_details": ["install simulation report is missing"], + }, + ) + return { + "target": target, + "status": "pass" if not failures else "fail", + "adapter": display_path(adapter_path, skill_dir), + "permission_model": str(target_contract.get("permission_model", "")), + "native_enforcement": bool(native) if isinstance(native, bool) else None, + "metadata_fallback_explicit": metadata_fallback, + "installer_enforcement": installer_enforcement, + "assurance": assurance, + "declared_capabilities": sorted_strings(target_contract.get("declared_capabilities")), + "checks": checks, + "failures": failures, + "residual_risks": residual_risks, + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + installer = report.get("installer_enforcement", {}) + lines = [ + "# Runtime Permission Probes", + "", + "Runtime permission probes verify that generated target adapters expose high-permission capabilities, make native-enforcement limits explicit, and link installer enforcement evidence when available.", + "", + "## Summary", + "", + f"- OK: `{report['ok']}`", + f"- Targets probed: `{summary['target_count']}`", + f"- Passed: `{summary['pass_count']}`", + f"- Failed: `{summary['fail_count']}`", + f"- Native enforcement targets: `{summary['native_enforcement_count']}`", + f"- Explicit metadata fallbacks: `{summary['metadata_fallback_count']}`", + f"- Installer enforcement source: `{summary['installer_enforcement_source_status']}`", + f"- Installer-enforced targets: `{summary['installer_enforcement_pass_count']}`", + f"- Installer permission failures: `{summary['installer_permission_failure_count']}`", + f"- World-class native evidence ready: `{summary['world_class_native_evidence_ready']}`", + f"- Required capabilities: `{', '.join(report['expected_capabilities']) or 'none'}`", + "", + "| Target | Status | Assurance | Native Enforcement | Metadata Fallback | Installer Enforcement | Residual Risk |", + "| --- | --- | --- | --- | --- | --- | --- |", + ] + for target in report["targets"]: + residual = "
    ".join(target["residual_risks"]) if target["residual_risks"] else "None" + installer_status = target.get("installer_enforcement", {}) + installer_label = "pass" if installer_status.get("enforced") else installer_status.get("source_status", "missing") + lines.append( + f"| `{target['target']}` | `{target['status']}` | `{target['assurance']}` | " + f"`{target['native_enforcement']}` | `{target['metadata_fallback_explicit']}` | `{installer_label}` | {residual} |" + ) + lines.extend( + [ + "", + "## Installer Enforcement", + "", + f"- Source: `{installer.get('source', '')}`", + f"- Source status: `{installer.get('source_status', 'missing')}`", + f"- Package dir matches probe: `{installer.get('package_dir_matches', False)}`", + "", + "Installer enforcement means the package install simulation blocks missing capability approvals or target enforcement notes. It is supporting local distribution evidence, not proof of target-client native enforcement.", + ] + ) + lines.extend(["", "## Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) + lines.extend( + [ + "", + "## Reviewer Note", + "", + "A passing probe means the target contract is explicit and auditable. It does not claim that a host client enforces permissions natively.", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def probe_runtime_permissions( + skill_dir: Path, + package_dir: Path, + targets: list[str], + output_json: Path, + output_md: Path, + install_simulation_json: Path | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + package_dir = package_dir.resolve() + expected = expected_capabilities(skill_dir) + installer = installer_enforcement_source(skill_dir, package_dir, targets, expected, install_simulation_json) + installer_targets = installer.get("targets", {}) if isinstance(installer.get("targets"), dict) else {} + target_results = [probe_target(skill_dir, package_dir, target, expected, installer_targets) for target in targets] + failures = [failure for target in target_results for failure in target["failures"]] + installer_summary = installer.get("summary", {}) if isinstance(installer.get("summary"), dict) else {} + installer_pass_count = sum( + 1 + for item in installer_targets.values() + if isinstance(item, dict) and item.get("enforced") is True + ) + summary = { + "target_count": len(target_results), + "pass_count": sum(1 for item in target_results if item["status"] == "pass"), + "fail_count": sum(1 for item in target_results if item["status"] == "fail"), + "native_enforcement_count": sum(1 for item in target_results if item["native_enforcement"] is True), + "metadata_fallback_count": sum(1 for item in target_results if item["metadata_fallback_explicit"]), + "residual_risk_count": sum(len(item["residual_risks"]) for item in target_results), + "required_capability_count": len(expected), + "failure_count": len(failures), + "installer_enforcement_source_status": installer.get("source_status", "missing"), + "installer_enforcement_target_count": len(installer_targets), + "installer_enforcement_pass_count": installer_pass_count, + "installer_permission_enforced_count": int(installer_summary.get("installer_permission_enforced_count", 0) or 0), + "installer_permission_failure_count": int(installer_summary.get("installer_permission_failure_count", 0) or 0), + "installer_permission_capability_count": int(installer_summary.get("permission_capability_count", 0) or 0), + "world_class_native_evidence_ready": sum(1 for item in target_results if item["native_enforcement"] is True) > 0 and not failures, + "installer_enforcement_ready": ( + installer.get("source_status") == "present" + and installer_pass_count == len(target_results) + and bool(expected) + and int(installer_summary.get("failure_count", 0) or 0) == 0 + and int(installer_summary.get("installer_permission_failure_count", 0) or 0) == 0 + ), + } + report = { + "schema_version": "1.0", + "ok": not failures, + "skill_dir": display_path(skill_dir, skill_dir), + "package_dir": display_path(package_dir, skill_dir), + "expected_capabilities": expected, + "summary": summary, + "installer_enforcement": { + "source": installer.get("source", ""), + "source_status": installer.get("source_status", "missing"), + "package_dir_matches": installer.get("package_dir_matches", False), + "summary": installer_summary, + }, + "targets": target_results, + "failures": failures, + "artifacts": { + "json": display_path(output_json, skill_dir), + "markdown": display_path(output_md, skill_dir), + }, + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Probe generated target adapters for runtime permission enforcement metadata.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--package-dir", default="dist") + parser.add_argument("--target", action="append", choices=DEFAULT_TARGETS) + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--install-simulation-json") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = probe_runtime_permissions( + skill_dir, + Path(args.package_dir).resolve(), + args.target or DEFAULT_TARGETS, + Path(args.output_json).resolve() if args.output_json else skill_dir / "reports" / "runtime_permission_probes.json", + Path(args.output_md).resolve() if args.output_md else skill_dir / "reports" / "runtime_permission_probes.md", + Path(args.install_simulation_json).resolve() if args.install_simulation_json else None, + ) + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/promotion_checker.py b/scripts/promotion_checker.py new file mode 100644 index 0000000..9990b4d --- /dev/null +++ b/scripts/promotion_checker.py @@ -0,0 +1,417 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + +OPTIMIZATION_REPORTS = { + "yao-meta-skill": ROOT / "reports" / "description_optimization.json", + "team-frontend-review": ROOT / "examples" / "team-frontend-review" / "optimization" / "reports" / "description_optimization.json", + "governed-incident-command": ROOT + / "examples" + / "governed-incident-command" + / "optimization" + / "reports" + / "description_optimization.json", +} + +SKILL_PATHS = { + "yao-meta-skill": ROOT / "SKILL.md", + "team-frontend-review": ROOT / "examples" / "team-frontend-review" / "generated-skill" / "SKILL.md", + "governed-incident-command": ROOT / "examples" / "governed-incident-command" / "generated-skill" / "SKILL.md", +} + + +def load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def error_total(prefix: str, target: dict) -> int: + return int(target.get(f"{prefix}_fp", 0)) + int(target.get(f"{prefix}_fn", 0)) + + +def family_clean(summary: dict | None) -> bool: + if not summary: + return False + return summary.get("clean_family_count", 0) == summary.get("family_count", 0) + + +def relative(path: Path) -> str: + return str(path.relative_to(ROOT)) + + +def classify_target(target: dict, route_scorecard: dict) -> dict: + route_summary = route_scorecard["summary"] + route_stats = route_scorecard["route_stats"].get(target["name"], {}) + winner_changed = target["winner_label"] != "Current" + visible_ok = error_total("winner_holdout", target) <= error_total("current_holdout", target) and error_total( + "winner_holdout", target + ) <= error_total("baseline_holdout", target) + blind_ok = error_total("winner_blind_holdout", target) <= error_total( + "current_blind_holdout", target + ) and error_total("winner_blind_holdout", target) <= error_total("baseline_blind_holdout", target) + judge_ok = error_total("winner_judge_blind_holdout", target) <= error_total( + "current_judge_blind_holdout", target + ) and error_total("winner_judge_blind_holdout", target) <= error_total("baseline_judge_blind_holdout", target) + judge_agreement = ((target.get("judge_blind") or {}).get("winner") or {}).get("agreement_rate") + judge_agreement_ok = judge_agreement is not None and judge_agreement >= 0.95 + adversarial_ok = error_total("winner_adversarial_holdout", target) <= error_total( + "current_adversarial_holdout", target + ) and error_total("winner_adversarial_holdout", target) <= error_total("baseline_adversarial_holdout", target) + holdout_calibration = (target.get("calibration") or {}).get("holdout") or {} + adversarial_calibration = (target.get("calibration") or {}).get("adversarial_holdout") or {} + adversarial_risk = adversarial_calibration.get("risk_band") + adversarial_risk_ok = adversarial_risk not in {"overlap", None} + route_clean = ( + route_summary.get("misroute_count", 0) == 0 + and route_summary.get("ambiguous_case_count", 0) == 0 + and route_stats.get("precision") == 1.0 + and route_stats.get("recall") == 1.0 + ) + family_ok = all( + family_clean((target.get("family_health") or {}).get(gate)) + for gate in ("blind_holdout", "judge_blind_holdout", "adversarial_holdout") + ) + + causes = [] + if not winner_changed: + causes.append("no_candidate_outperformed_current") + if not visible_ok: + causes.append("visible_holdout_regression") + if not blind_ok: + causes.append("blind_holdout_regression") + if error_total("winner_holdout", target) > 0: + causes.append("current_holdout_gap_present") + if holdout_calibration.get("risk_band") in {"overlap", "watch", "tight"}: + causes.append("current_holdout_risk") + if not judge_ok: + causes.append("judge_blind_regression") + if not judge_agreement_ok: + causes.append("judge_blind_low_agreement") + if not adversarial_ok: + causes.append("adversarial_regression") + if adversarial_risk == "overlap": + causes.append("adversarial_overlap_risk") + elif adversarial_risk in {"watch", "tight"}: + causes.append("adversarial_watch_risk") + if not family_ok: + causes.append("family_instability") + if route_summary.get("misroute_count", 0): + causes.append("route_confusion") + if route_summary.get("ambiguous_case_count", 0): + causes.append("route_ambiguity") + if target["winner_tokens"] > target["current_tokens"] and not winner_changed: + causes.append("longer_without_gain") + + severe = ( + route_summary.get("misroute_count", 0) > 0 + or route_summary.get("ambiguous_case_count", 0) > 0 + or adversarial_risk == "overlap" + or not judge_agreement_ok + ) + + all_gates_pass = all( + [visible_ok, blind_ok, judge_ok, judge_agreement_ok, adversarial_ok, adversarial_risk_ok, route_clean, family_ok] + ) + + if winner_changed and all_gates_pass: + decision = "promote" + elif severe: + decision = "blocked" + else: + decision = "keep_current" + + if decision == "promote" and "promotion_ready" not in causes: + causes.append("promotion_ready") + + next_action = { + "promote": "Review the promoted candidate, update the skill description, and snapshot the release.", + "keep_current": "Keep the current description and open a new candidate only when fresh route evidence appears.", + "blocked": "Do not promote. Investigate failing gates, route collisions, or calibration risk before generating new candidates.", + }[decision] + + return { + "decision": decision, + "winner_changed": winner_changed, + "all_gates_pass": all_gates_pass, + "route_clean": route_clean, + "judge_agreement_ok": judge_agreement_ok, + "causes": causes, + "next_action": next_action, + "gate_status": { + "visible_holdout_non_regression": visible_ok, + "blind_holdout_non_regression": blind_ok, + "judge_blind_non_regression": judge_ok, + "judge_blind_agreement": judge_agreement_ok, + "adversarial_non_regression": adversarial_ok, + "adversarial_risk_ok": adversarial_risk_ok, + "route_confusion_clean": route_clean, + "family_stability": family_ok, + }, + "route_metrics": { + "precision": route_stats.get("precision"), + "recall": route_stats.get("recall"), + "average_margin": route_stats.get("average_margin"), + "misroute_count": route_summary.get("misroute_count"), + "ambiguous_case_count": route_summary.get("ambiguous_case_count"), + }, + } + + +def build_candidate_entries(target_name: str, target_summary: dict, decision: str) -> list[dict]: + report = load_json(OPTIMIZATION_REPORTS[target_name]) + winner_description = report["winner"]["description"] + winner_dev_errors = report["winner"]["dev"]["false_positives"] + report["winner"]["dev"]["false_negatives"] + entries = [ + { + "name": target_name, + "role": "baseline", + "label": "Baseline", + "ranking_state": "reference", + "promotion_state": "reference", + "description": report["baseline"]["description"] if report.get("baseline") else None, + "tokens": report["baseline"]["estimated_tokens"] if report.get("baseline") else None, + "dev_errors": ( + report["baseline"]["dev"]["false_positives"] + report["baseline"]["dev"]["false_negatives"] + if report.get("baseline") + else None + ), + "holdout_errors": ( + report["baseline"]["holdout"]["false_positives"] + report["baseline"]["holdout"]["false_negatives"] + if report.get("baseline") and report["baseline"].get("holdout") + else None + ), + } + ] + + for candidate in report["candidates"]: + selected = candidate["description"] == winner_description + role = "current" if candidate["id"] == "current" else "candidate" + if selected and decision == "promote": + promotion_state = "promoted" + elif selected and role == "current": + promotion_state = "kept_current" + elif selected: + promotion_state = "blocked" + else: + promotion_state = "blocked" + + reason_tags = [] + candidate_dev_errors = candidate["dev"]["false_positives"] + candidate["dev"]["false_negatives"] + if candidate_dev_errors > winner_dev_errors: + reason_tags.append("weaker_dev_fit") + holdout = candidate.get("holdout") or {} + holdout_errors = holdout.get("false_positives", 0) + holdout.get("false_negatives", 0) if holdout else None + if holdout_errors is not None and holdout_errors > error_total("current_holdout", target_summary): + reason_tags.append("visible_holdout_regression") + if candidate["estimated_tokens"] > target_summary["current_tokens"] and not selected: + reason_tags.append("longer_without_gain") + if not reason_tags and not selected: + reason_tags.append("not_selected_by_dev_ranking") + + entries.append( + { + "name": target_name, + "role": role, + "label": candidate["label"], + "ranking_state": "selected_by_dev" if selected else "not_selected", + "promotion_state": promotion_state, + "description": candidate["description"], + "tokens": candidate["estimated_tokens"], + "dev_errors": candidate_dev_errors, + "holdout_errors": holdout_errors, + "reason_tags": reason_tags, + } + ) + return entries + + +def render_candidate_registry_md(targets: list[dict]) -> str: + lines = [ + "# Candidate Registry", + "", + "| Target | Role | Label | Ranking State | Promotion State | Tokens | Dev Errors | Holdout Errors | Reason Tags |", + "| --- | --- | --- | --- | --- | ---: | ---: | ---: | --- |", + ] + for target in targets: + for entry in target["registry"]: + lines.append( + f"| `{target['name']}` | {entry['role']} | `{entry['label']}` | {entry['ranking_state']} | {entry['promotion_state']} | {'-' if entry['tokens'] is None else entry['tokens']} | {'-' if entry['dev_errors'] is None else entry['dev_errors']} | {'-' if entry['holdout_errors'] is None else entry['holdout_errors']} | {', '.join(entry.get('reason_tags', [])) or '-'} |" + ) + return "\n".join(lines) + "\n" + + +def render_promotion_md(targets: list[dict]) -> str: + lines = [ + "# Promotion Decisions", + "", + "| Target | Decision | Winner | Route Clean | Judge Agreement | Adv Risk | Causes | Next Action |", + "| --- | --- | --- | --- | --- | --- | --- | --- |", + ] + for target in targets: + decision = target["decision"] + judge_summary = ((target.get("judge_blind") or {}).get("winner") or {}).get("agreement_rate") + adv_risk = ((target.get("calibration") or {}).get("adversarial_holdout") or {}).get("risk_band") + lines.append( + f"| `{target['name']}` | `{decision}` | `{target['winner_label']}` | {target['promotion']['route_clean']} | {judge_summary if judge_summary is not None else '-'} | {adv_risk or '-'} | {', '.join(target['promotion']['causes'])} | {target['promotion']['next_action']} |" + ) + return "\n".join(lines) + "\n" + + +def render_bundle_md(bundle: dict) -> str: + review = bundle["review_template"] + lines = [ + f"# Iteration Bundle: {bundle['target']}", + "", + f"- decision: `{bundle['decision']}`", + f"- winner label: `{bundle['winner_label']}`", + f"- winner changed: `{bundle['winner_changed']}`", + f"- next action: {bundle['next_action']}", + "", + "## Cause Tags", + "", + ] + for cause in bundle["cause_tags"]: + lines.append(f"- `{cause}`") + lines.extend( + [ + "", + "## Gate Status", + "", + "| Gate | Pass |", + "| --- | --- |", + ] + ) + for gate, passed in bundle["gate_status"].items(): + lines.append(f"| `{gate}` | {passed} |") + lines.extend( + [ + "", + "## Candidate Registry", + "", + "| Role | Label | Ranking State | Promotion State | Tokens | Dev Errors | Holdout Errors |", + "| --- | --- | --- | --- | ---: | ---: | ---: |", + ] + ) + for entry in bundle["candidate_registry"]: + lines.append( + f"| {entry['role']} | `{entry['label']}` | {entry['ranking_state']} | {entry['promotion_state']} | {'-' if entry['tokens'] is None else entry['tokens']} | {'-' if entry['dev_errors'] is None else entry['dev_errors']} | {'-' if entry['holdout_errors'] is None else entry['holdout_errors']} |" + ) + lines.extend( + [ + "", + "## Human Review Stub", + "", + f"- target: {review['target']}", + f"- current description: {review['current_description']}", + f"- candidate description: {review['candidate_description']}", + f"- review focus: {', '.join(review['focus'])}", + "", + "## Artifact Paths", + "", + ] + ) + for label, path in bundle["artifacts"].items(): + lines.append(f"- {label}: `{path}`") + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Apply promotion policy and build iteration bundles.") + parser.add_argument("--optimization-suite", default="reports/description_optimization_suite.json") + parser.add_argument("--route-scorecard", default="reports/route_scorecard.json") + parser.add_argument("--output-json", default="reports/promotion_decisions.json") + parser.add_argument("--output-md", default="reports/promotion_decisions.md") + parser.add_argument("--candidate-registry-json", default="reports/candidate_registry.json") + parser.add_argument("--candidate-registry-md", default="reports/candidate_registry.md") + parser.add_argument("--bundle-dir", default="reports/iteration_bundles") + args = parser.parse_args() + + suite = load_json(ROOT / args.optimization_suite) + route_scorecard = load_json(ROOT / args.route_scorecard) + bundle_dir = ROOT / args.bundle_dir + bundle_dir.mkdir(parents=True, exist_ok=True) + + targets = [] + decisions = [] + for target in suite["targets"]: + promotion = classify_target(target, route_scorecard) + registry = build_candidate_entries(target["name"], target, promotion["decision"]) + target["promotion"] = promotion + target["registry"] = registry + target["decision"] = promotion["decision"] + targets.append(target) + decisions.append(promotion["decision"]) + + bundle = { + "target": target["name"], + "decision": promotion["decision"], + "winner_label": target["winner_label"], + "winner_changed": promotion["winner_changed"], + "next_action": promotion["next_action"], + "cause_tags": promotion["causes"], + "gate_status": promotion["gate_status"], + "candidate_registry": registry, + "review_template": { + "target": target["name"], + "current_description": target["winner_description"] if target["winner_label"] == "Current" else load_json(OPTIMIZATION_REPORTS[target["name"]])["current_description"], + "candidate_description": target["winner_description"], + "focus": promotion["causes"][:3] or ["promotion_ready"], + }, + "artifacts": { + "skill": relative(SKILL_PATHS[target["name"]]), + "optimization_report": relative(OPTIMIZATION_REPORTS[target["name"]]), + "promotion_decisions": relative(ROOT / args.output_json), + "candidate_registry": relative(ROOT / args.candidate_registry_json), + "regression_cause_taxonomy": "references/regression-cause-taxonomy.md", + "human_review_template": "references/human-review-template.md", + }, + } + target_dir = bundle_dir / target["name"] + target_dir.mkdir(parents=True, exist_ok=True) + (target_dir / "bundle.json").write_text(json.dumps(bundle, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + (target_dir / "bundle.md").write_text(render_bundle_md(bundle), encoding="utf-8") + (target_dir / "review.md").write_text( + "\n".join( + [ + "# Human Review", + "", + f"- target: {bundle['review_template']['target']}", + f"- current description: {bundle['review_template']['current_description']}", + f"- candidate description: {bundle['review_template']['candidate_description']}", + f"- suggested focus: {', '.join(bundle['review_template']['focus'])}", + "", + "Use the shared template in `references/human-review-template.md` to complete the final decision.", + "", + ] + ), + encoding="utf-8", + ) + + summary = { + "ok": "blocked" not in decisions, + "summary": { + "promote": sum(1 for d in decisions if d == "promote"), + "keep_current": sum(1 for d in decisions if d == "keep_current"), + "blocked": sum(1 for d in decisions if d == "blocked"), + }, + "targets": targets, + } + + output_json = ROOT / args.output_json + output_md = ROOT / args.output_md + candidate_json = ROOT / args.candidate_registry_json + candidate_md = ROOT / args.candidate_registry_md + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_promotion_md(targets), encoding="utf-8") + candidate_json.write_text(json.dumps({"targets": targets}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + candidate_md.write_text(render_candidate_registry_md(targets), encoding="utf-8") + print(json.dumps(summary, ensure_ascii=False, indent=2)) + if not summary["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/propose_adaptation.py b/scripts/propose_adaptation.py new file mode 100644 index 0000000..67903e9 --- /dev/null +++ b/scripts/propose_adaptation.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Turns redacted repeated preference patterns into proposal-only adaptation plans." + +PROPOSAL_LIBRARY = { + "language_default": { + "title": "Keep reports Chinese-first with optional English", + "change_type": "report-default-language", + "risk_level": "low", + "target_files": ["scripts/render_skill_overview.py", "references/artifact-design-doctrine.md"], + "suggested_changes": [ + "Keep user-facing report copy Simplified Chinese by default.", + "Expose English through the existing language switch instead of mixing languages in the default view.", + ], + "verification_commands": ["python3 tests/verify_skill_overview.py"], + "rollback_plan": "Revert report language template changes and rerun the overview verifier.", + }, + "report_ui": { + "title": "Improve report layout, visual hierarchy, and chart readability", + "change_type": "artifact-ui-polish", + "risk_level": "medium", + "target_files": ["scripts/render_skill_overview.py", "references/artifact-design-doctrine.md", "tests/verify_skill_overview.py"], + "suggested_changes": [ + "Prefer vertical narrative sections with limited two-column layouts only when content has enough width.", + "Keep charts inline SVG, with captions and stable responsive constraints.", + ], + "verification_commands": ["python3 tests/verify_skill_overview.py", "python3 tests/verify_skill_report_charts.py"], + "rollback_plan": "Restore the previous report renderer and regenerate the demo report.", + }, + "approval_safety": { + "title": "Keep adaptive iteration approval-gated", + "change_type": "privacy-governance", + "risk_level": "low", + "target_files": ["references/user-memory-policy.md", "references/autonomous-adaptation.md", "schemas/adaptation-proposal.schema.json"], + "suggested_changes": [ + "Require explicit source paths for memory scans.", + "Generate proposals before any source patching.", + "Reserve automatic apply for a future approval ledger and rollback implementation.", + ], + "verification_commands": ["python3 tests/verify_adaptation_safety.py"], + "rollback_plan": "Remove the adaptive proposal artifacts and keep feedback/adoption drift as the only iteration inputs.", + }, + "delivery_format": { + "title": "Make generated artifact paths explicit in CLI output", + "change_type": "artifact-discoverability", + "risk_level": "low", + "target_files": ["scripts/yao.py", "README.md"], + "suggested_changes": [ + "Include stable report paths in command output.", + "Document which artifacts are meant for human review.", + ], + "verification_commands": ["python3 tests/verify_yao_cli.py"], + "rollback_plan": "Revert CLI copy/documentation changes and keep artifact paths unchanged.", + }, + "evidence_testing": { + "title": "Attach tests and evidence refresh to each upgrade", + "change_type": "quality-gate", + "risk_level": "medium", + "target_files": ["tests/verify_adaptation_safety.py", "scripts/render_skill_os2_coverage.py", "reports/skill_os2_coverage.json"], + "suggested_changes": [ + "Add focused verifier coverage for every new adaptive behavior.", + "Refresh Skill OS 2.0 coverage so planned, partial, and covered states remain visible.", + ], + "verification_commands": ["python3 tests/verify_adaptation_safety.py", "python3 tests/verify_skill_os2_coverage.py"], + "rollback_plan": "Revert the new verifier and coverage status updates, then regenerate coverage reports.", + }, +} + +TOP_LEVEL_SUMMARY_FIELDS = [ + "pattern_count", + "proposal_count", + "apply_supported", + "failure_count", +] +TOP_LEVEL_PROPOSAL_CONTRACT_FIELDS = [ + "proposal_only", + "approval_required", + "writes_repository_files", + "allowlisted_targets_required", + "target_file_sha256_required_for_apply", + "approval_draft_supported", + "rollback_required_for_apply", + "apply_command_available", +] + + +def top_level_mirrors(summary: dict[str, Any], proposal_contract: dict[str, Any]) -> dict[str, Any]: + mirrored = {key: summary[key] for key in TOP_LEVEL_SUMMARY_FIELDS if key in summary} + mirrored.update({key: proposal_contract[key] for key in TOP_LEVEL_PROPOSAL_CONTRACT_FIELDS if key in proposal_contract}) + return mirrored + + +def report_contract() -> dict[str, Any]: + return { + "schema_version": "1.0", + "contract": "adaptation-proposals", + "top_level_mirrors_summary": True, + "top_level_mirrors_proposal_contract": True, + "summary_fields": TOP_LEVEL_SUMMARY_FIELDS, + "proposal_contract_fields": TOP_LEVEL_PROPOSAL_CONTRACT_FIELDS, + "source_of_truth": ["summary", "proposal_contract"], + } + + +def utc_now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def display_path(path: Path, skill_dir: Path) -> str: + try: + return str(path.resolve().relative_to(skill_dir.resolve())) + except ValueError: + return str(path.resolve()) + + +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 proposal_id(pattern_id: str, support_count: int, target_files: list[str]) -> str: + digest = hashlib.sha1(f"{pattern_id}:{support_count}:{','.join(target_files)}".encode("utf-8")).hexdigest() + return f"adapt-{digest[:10]}" + + +def proposal_from_pattern(pattern: dict[str, Any]) -> dict[str, Any]: + pattern_id = str(pattern.get("pattern_id") or "generic") + spec = PROPOSAL_LIBRARY.get( + pattern_id, + { + "title": "Review repeated preference pattern", + "change_type": "manual-review", + "risk_level": "medium", + "target_files": ["README.md"], + "suggested_changes": ["Review this repeated signal manually before changing skill behavior."], + "verification_commands": ["make ci-test"], + "rollback_plan": "Revert the approved patch and rerun the relevant verifier.", + }, + ) + support_count = int(pattern.get("support_count") or 0) + target_files = list(spec["target_files"]) + return { + "proposal_id": proposal_id(pattern_id, support_count, target_files), + "pattern_id": pattern_id, + "title": spec["title"], + "change_type": spec["change_type"], + "status": "proposal-only", + "requires_approval": True, + "write_allowed_without_approval": False, + "risk_level": spec["risk_level"], + "reason": pattern.get("reason", "Repeated signal detected."), + "support_count": support_count, + "target_files": target_files, + "suggested_changes": spec["suggested_changes"], + "verification_commands": spec["verification_commands"], + "rollback_plan": spec["rollback_plan"], + "evidence_refs": [ + { + "record_id": item.get("record_id", "unknown"), + "excerpt": item.get("excerpt", ""), + } + for item in pattern.get("evidence", [])[:3] + if isinstance(item, dict) + ], + } + + +def build_report(skill_dir: Path, patterns_json: Path, generated_at: str) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + if not patterns_json.is_absolute(): + patterns_json = skill_dir / patterns_json + patterns_payload = load_json(patterns_json) + failures: list[str] = [] + if not patterns_payload: + failures.append(f"Pattern report does not exist or is invalid: {display_path(patterns_json, skill_dir)}") + elif patterns_payload.get("ok") is not True: + failures.append("Pattern report is not ok; fix scan failures before proposal generation.") + patterns = patterns_payload.get("patterns", []) if isinstance(patterns_payload.get("patterns"), list) else [] + proposals = [proposal_from_pattern(pattern) for pattern in patterns if isinstance(pattern, dict)] if not failures else [] + summary = { + "pattern_count": len(patterns), + "proposal_count": len(proposals), + "apply_supported": (ROOT / "scripts" / "apply_adaptation.py").exists(), + "failure_count": len(failures), + } + proposal_contract = { + "proposal_only": True, + "approval_required": True, + "writes_repository_files": False, + "allowlisted_targets_required": True, + "target_file_sha256_required_for_apply": True, + "approval_draft_supported": True, + "rollback_required_for_apply": True, + "apply_command_available": (ROOT / "scripts" / "apply_adaptation.py").exists(), + } + return { + "schema_version": "1.0", + "ok": not failures, + "generated_at": generated_at, + "skill_dir": display_path(skill_dir, skill_dir), + "source_patterns": display_path(patterns_json, skill_dir), + **top_level_mirrors(summary, proposal_contract), + "summary": summary, + "proposal_contract": proposal_contract, + "report_contract": report_contract(), + "proposals": proposals, + "failures": failures, + "artifacts": { + "json": "reports/adaptation_proposals.json", + "markdown": "reports/adaptation_proposals.md", + }, + } + + +def render_markdown(report: dict[str, Any]) -> str: + lines = [ + "# Adaptation Proposals", + "", + f"- Generated at: `{report['generated_at']}`", + f"- Pattern report: `{report['source_patterns']}`", + f"- Proposal only: `{str(report['proposal_contract']['proposal_only']).lower()}`", + f"- Writes repository files: `{str(report['proposal_contract']['writes_repository_files']).lower()}`", + f"- Proposals: `{report['summary']['proposal_count']}`", + "", + ] + if not report["proposals"]: + lines.append("No proposals were generated.") + for proposal in report["proposals"]: + lines.extend( + [ + f"## {proposal['title']}", + "", + f"- ID: `{proposal['proposal_id']}`", + f"- Status: `{proposal['status']}`", + f"- Pattern: `{proposal['pattern_id']}`", + f"- Risk: `{proposal['risk_level']}`", + f"- Requires approval: `{str(proposal['requires_approval']).lower()}`", + f"- Reason: {proposal['reason']}", + "- Target files:", + ] + ) + lines.extend(f" - `{path}`" for path in proposal["target_files"]) + lines.append("- Suggested changes:") + lines.extend(f" - {item}" for item in proposal["suggested_changes"]) + lines.append("- Verification:") + lines.extend(f" - `{item}`" for item in proposal["verification_commands"]) + lines.append(f"- Rollback: {proposal['rollback_plan']}") + if proposal["evidence_refs"]: + lines.append("- Redacted evidence refs:") + lines.extend(f" - `{item['record_id']}`: {item['excerpt']}" for item in proposal["evidence_refs"]) + lines.append("") + 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="Create proposal-only adaptation plans from summarized user signal patterns.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--patterns-json", default="reports/user_patterns.json") + parser.add_argument("--output-json", default="reports/adaptation_proposals.json") + parser.add_argument("--output-md", default="reports/adaptation_proposals.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, Path(args.patterns_json), args.generated_at) + if report["ok"]: + output_json = resolve_output(skill_dir, args.output_json) + output_md = resolve_output(skill_dir, args.output_md) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + report["artifacts"] = { + "json": display_path(output_json, skill_dir), + "markdown": display_path(output_md, skill_dir), + } + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/provider_output_eval_runner.py b/scripts/provider_output_eval_runner.py new file mode 100644 index 0000000..4423a7a --- /dev/null +++ b/scripts/provider_output_eval_runner.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import sys +from pathlib import Path +from typing import Any +from urllib.error import HTTPError, URLError +from urllib.parse import urlparse +from urllib.request import Request, urlopen + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1/responses" +DEFAULT_DEEPSEEK_BASE_URL = "https://api.deepseek.com/chat/completions" +DEFAULT_BASE_URL = DEFAULT_OPENAI_BASE_URL +DEFAULT_HOST = urlparse(DEFAULT_OPENAI_BASE_URL).hostname or "api.openai.com" +DEFAULT_PROVIDER_CONFIGS = { + "openai": { + "base_url": DEFAULT_OPENAI_BASE_URL, + "api_format": "responses", + "api_key_env": "OPENAI_API_KEY", + "thinking": "", + }, + "deepseek": { + "base_url": DEFAULT_DEEPSEEK_BASE_URL, + "api_format": "chat-completions", + "api_key_env": "DEEPSEEK_API_KEY", + "thinking": "disabled", + }, +} +ALLOWED_PATH_PREFIX = "/v1/responses" +CHAT_COMPLETIONS_PATHS = {"/chat/completions", "/v1/chat/completions"} +LOCAL_HOSTS = {"127.0.0.1", "localhost", "::1"} + + +def fail(message: str) -> None: + print(message, file=sys.stderr) + raise SystemExit(2) + + +def validate_base_url( + base_url: str, + allow_insecure_localhost: bool, + allow_custom_base_url: bool, + api_format: str, +) -> None: + parsed = urlparse(base_url) + host = parsed.hostname or "" + if api_format == "responses" and not parsed.path.startswith(ALLOWED_PATH_PREFIX): + fail(f"provider endpoint path must start with {ALLOWED_PATH_PREFIX}") + if api_format == "chat-completions" and parsed.path not in CHAT_COMPLETIONS_PATHS: + fail("chat-completions provider endpoint path must be /chat/completions or /v1/chat/completions") + if parsed.scheme == "https" and ( + base_url in {DEFAULT_OPENAI_BASE_URL, DEFAULT_DEEPSEEK_BASE_URL} + or host == DEFAULT_HOST + or allow_custom_base_url + ): + return + if parsed.scheme == "https": + fail("custom provider host requires --allow-custom-base-url") + if parsed.scheme == "http" and allow_insecure_localhost and (parsed.hostname or "") in LOCAL_HOSTS: + return + fail("provider runner requires HTTPS; use --allow-insecure-localhost only for local test servers") + + +def provider_defaults(provider: str) -> dict[str, str]: + return DEFAULT_PROVIDER_CONFIGS.get(provider, DEFAULT_PROVIDER_CONFIGS["openai"]) + + +def load_request() -> dict[str, Any]: + raw = sys.stdin.read() + if not raw.strip(): + fail("provider runner requires a JSON request on stdin") + try: + payload = json.loads(raw) + except json.JSONDecodeError as exc: + fail(f"invalid JSON request: {exc}") + if not isinstance(payload, dict): + fail("runner request must be a JSON object") + return payload + + +def safe_relative(path_value: str) -> Path | None: + path = Path(path_value) + if path.is_absolute() or ".." in path.parts: + return None + return path + + +def read_input_files(paths: Any, input_root: Path, max_chars: int) -> list[dict[str, str]]: + if not isinstance(paths, list): + return [] + files: list[dict[str, str]] = [] + for item in paths: + rel = safe_relative(str(item)) + if rel is None: + files.append({"path": str(item), "status": "skipped-unsafe-path", "content": ""}) + continue + path = input_root / rel + if not path.exists() or not path.is_file(): + files.append({"path": str(item), "status": "missing", "content": ""}) + continue + text = path.read_text(encoding="utf-8", errors="replace") + files.append({"path": str(item), "status": "loaded", "content": text[:max_chars]}) + return files + + +def read_skill_instructions(path: Path, max_chars: int) -> str: + if not path.exists() or not path.is_file(): + return "" + return path.read_text(encoding="utf-8", errors="replace")[:max_chars] + + +def build_provider_input(request: dict[str, Any], skill_text: str, input_files: list[dict[str, str]]) -> str: + variant = str(request.get("variant", "")) + lines = [ + "You are producing one output for a Yao Meta Skill output-eval case.", + f"Case id: {request.get('case_id', '')}", + f"Variant: {variant}", + "", + "User task:", + str(request.get("prompt", "")), + "", + ] + if variant == "with_skill": + lines.extend( + [ + "Use the skill instructions below as the operating guidance. Preserve concrete evidence paths and boundaries.", + "", + "Skill instructions:", + skill_text or "(Skill instructions were not available.)", + "", + ] + ) + else: + lines.extend( + [ + "Produce a direct baseline answer without using the Yao Meta Skill guidance.", + "Do not invent files, reports, governance evidence, or hidden review artifacts.", + "", + ] + ) + if input_files: + lines.append("Input files:") + for item in input_files: + lines.append(f"--- {item['path']} ({item['status']}) ---") + if item["content"]: + lines.append(item["content"]) + lines.append("") + lines.extend( + [ + "Return only the final user-facing answer for this variant.", + "Do not mention or copy any fixture output from the eval case.", + ] + ) + return "\n".join(lines) + + +def response_text(payload: dict[str, Any]) -> str: + if isinstance(payload.get("output_text"), str): + return str(payload["output_text"]) + parts: list[str] = [] + output = payload.get("output") + if isinstance(output, list): + for item in output: + if not isinstance(item, dict): + continue + content = item.get("content") + if not isinstance(content, list): + continue + for block in content: + if not isinstance(block, dict): + continue + if isinstance(block.get("text"), str): + parts.append(str(block["text"])) + if parts: + return "\n".join(part for part in parts if part).strip() + choices = payload.get("choices") + if isinstance(choices, list) and choices: + first = choices[0] + if isinstance(first, dict): + message = first.get("message", {}) + if isinstance(message, dict) and isinstance(message.get("content"), str): + return str(message["content"]) + return "" + + +def observed_usage(payload: dict[str, Any]) -> dict[str, Any]: + usage = payload.get("usage", {}) + if not isinstance(usage, dict): + return {} + input_tokens = usage.get("input_tokens", usage.get("prompt_tokens")) + output_tokens = usage.get("output_tokens", usage.get("completion_tokens")) + total_tokens = usage.get("total_tokens") + result: dict[str, Any] = {} + if input_tokens is not None: + result["input_tokens"] = int(input_tokens) + if output_tokens is not None: + result["output_tokens"] = int(output_tokens) + if total_tokens is not None: + result["total_tokens"] = int(total_tokens) + if result: + result["estimated"] = False + return result + + +def request_body( + model: str, + provider_input: str, + api_format: str, + thinking_type: str, + temperature: float | None, +) -> dict[str, Any]: + if api_format == "chat-completions": + body: dict[str, Any] = { + "model": model, + "messages": [{"role": "user", "content": provider_input}], + } + if thinking_type: + body["thinking"] = {"type": thinking_type} + if temperature is not None: + body["temperature"] = temperature + return body + body = {"model": model, "input": provider_input} + if temperature is not None: + body["temperature"] = temperature + return body + + +def call_provider( + base_url: str, + api_key: str, + model: str, + provider_input: str, + timeout_seconds: float, + api_format: str, + thinking_type: str, + temperature: float | None, +) -> dict[str, Any]: + body = json.dumps( + request_body(model, provider_input, api_format, thinking_type, temperature), + ensure_ascii=False, + ).encode("utf-8") + request = Request( + base_url, + data=body, + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + method="POST", + ) + try: + with urlopen(request, timeout=timeout_seconds) as response: + response_body = response.read().decode("utf-8", errors="replace") + except HTTPError as exc: + detail = exc.read().decode("utf-8", errors="replace")[:500] + fail(f"provider request failed with HTTP {exc.code}: {detail}") + except URLError as exc: + fail(f"provider request failed: {exc.reason}") + try: + payload = json.loads(response_body) + except json.JSONDecodeError as exc: + fail(f"provider returned invalid JSON: {exc}") + if not isinstance(payload, dict): + fail("provider response must be a JSON object") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser( + description=( + "Provider-backed output-eval runner for run_output_execution.py. " + "Requires real model credentials and reports execution_kind=model only after an HTTP provider call." + ) + ) + parser.add_argument("--provider", default="openai", help="Provider label to write into execution evidence.") + parser.add_argument( + "--base-url", + help="Override the provider endpoint. Defaults are selected from --provider.", + ) + parser.add_argument( + "--api-format", + choices=["responses", "chat-completions"], + help="Provider API shape. Defaults are selected from --provider.", + ) + parser.add_argument("--thinking", choices=["enabled", "disabled"], help="Optional chat-completions thinking mode.") + parser.add_argument("--temperature", type=float, default=0.0, help="Sampling temperature for provider requests.") + parser.add_argument("--model", default=os.environ.get("YAO_OUTPUT_EVAL_MODEL", "")) + parser.add_argument("--api-key-env", help="Environment variable that contains the provider API key.") + parser.add_argument("--input-root", default=str(ROOT / "evals" / "output")) + parser.add_argument("--skill-file", default=str(ROOT / "SKILL.md")) + parser.add_argument("--timeout-seconds", type=float, default=60.0) + parser.add_argument("--max-input-file-chars", type=int, default=6000) + parser.add_argument("--max-skill-chars", type=int, default=8000) + parser.add_argument("--allow-insecure-localhost", action="store_true") + parser.add_argument("--allow-custom-base-url", action="store_true") + args = parser.parse_args() + + defaults = provider_defaults(args.provider) + base_url = args.base_url or defaults["base_url"] + api_format = args.api_format or defaults["api_format"] + thinking = args.thinking if args.thinking is not None else defaults["thinking"] + api_key_env = args.api_key_env or defaults["api_key_env"] + + validate_base_url(base_url, args.allow_insecure_localhost, args.allow_custom_base_url, api_format) + if args.temperature is not None and not 0 <= args.temperature <= 2: + fail("--temperature must be between 0 and 2") + if not args.model: + fail("missing model; pass --model or set YAO_OUTPUT_EVAL_MODEL") + api_key = os.environ.get(api_key_env, "") + if not api_key: + fail(f"missing API key env: {api_key_env}") + + request = load_request() + input_files = read_input_files(request.get("input_files", []), Path(args.input_root).resolve(), args.max_input_file_chars) + skill_text = read_skill_instructions(Path(args.skill_file).resolve(), args.max_skill_chars) + provider_input = build_provider_input(request, skill_text, input_files) + response = call_provider( + base_url, + api_key, + args.model, + provider_input, + args.timeout_seconds, + api_format, + thinking, + args.temperature, + ) + output = response_text(response) + if not output: + fail("provider response did not contain output text") + result = { + "output": output, + "execution_kind": "model", + "provider": args.provider, + "model": args.model, + "usage": observed_usage(response), + "response_id": str(response.get("id", "")), + } + print(json.dumps(result, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/scripts/python_compat_check.py b/scripts/python_compat_check.py new file mode 100644 index 0000000..b6afaa7 --- /dev/null +++ b/scripts/python_compat_check.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +import argparse +import io +import json +import token +import tokenize +from datetime import date +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Checks repository Python source for syntax that can pass locally but fail on the supported CI interpreter." + +EXCLUDED_DIRS = { + ".git", + ".mypy_cache", + ".previews", + ".pytest_cache", + ".ruff_cache", + ".venv", + "__pycache__", + "dist", + "node_modules", + "venv", +} +MAX_FILE_BYTES = 1_000_000 + + +def rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def resolve_path(raw_path: str, root: Path) -> Path: + path = Path(raw_path) + if not path.is_absolute(): + path = root / path + return path.resolve() + + +def is_ignored(path: Path, root: Path) -> bool: + try: + parts = path.resolve().relative_to(root.resolve()).parts + except ValueError: + return True + if any(part in EXCLUDED_DIRS for part in parts): + return True + if len(parts) >= 2 and parts[0] == "tests" and parts[1].startswith("tmp"): + return True + return False + + +def expand_scan_paths(root: Path, raw_paths: list[str]) -> list[Path]: + candidates: list[Path] = [] + explicit_paths = bool(raw_paths) + if raw_paths: + for raw_path in raw_paths: + path = resolve_path(raw_path, root) + if path.is_dir(): + candidates.extend(path.rglob("*.py")) + elif path.exists(): + candidates.append(path) + else: + candidates.extend(root.rglob("*.py")) + files = [] + for path in candidates: + if not path.is_file() or path.is_symlink() or path.suffix != ".py": + continue + if not explicit_paths and is_ignored(path, root): + continue + try: + if path.stat().st_size > MAX_FILE_BYTES: + continue + except OSError: + continue + files.append(path) + return sorted(set(files), key=lambda item: rel_path(item, root)) + + +def issue(path: Path, root: Path, line: int, column: int, rule: str, message: str, excerpt: str = "") -> dict[str, Any]: + return { + "path": rel_path(path, root), + "line": line, + "column": column, + "rule": rule, + "message": message, + "excerpt": excerpt.strip()[:220], + } + + +def compile_issues(path: Path, root: Path, source: str) -> list[dict[str, Any]]: + try: + compile(source, str(path), "exec") + except SyntaxError as exc: + return [ + issue( + path, + root, + exc.lineno or 0, + exc.offset or 0, + "current-python-syntax", + str(exc.msg), + exc.text or "", + ) + ] + return [] + + +def token_name(token_type: int) -> str: + return token.tok_name.get(token_type, "") + + +def scan_modern_fstring_tokens(path: Path, root: Path, source: str) -> tuple[bool, list[dict[str, Any]]]: + findings: list[dict[str, Any]] = [] + try: + tokens = list(tokenize.generate_tokens(io.StringIO(source).readline)) + except tokenize.TokenError: + return False, findings + has_modern_fstring_tokens = any(token_name(item.type) == "FSTRING_START" for item in tokens) + if not has_modern_fstring_tokens: + return False, findings + fstring_depth = 0 + expression_depth = 0 + for item in tokens: + name = token_name(item.type) + if name == "FSTRING_START": + fstring_depth += 1 + expression_depth = 0 + continue + if name == "FSTRING_END": + fstring_depth = max(0, fstring_depth - 1) + expression_depth = 0 + continue + if not fstring_depth: + continue + if item.type == token.OP and item.string == "{": + expression_depth += 1 + continue + if item.type == token.OP and item.string == "}": + expression_depth = max(0, expression_depth - 1) + continue + if expression_depth > 0 and "\\" in item.string: + findings.append( + issue( + path, + root, + item.start[0], + item.start[1] + 1, + "fstring-expression-backslash", + "Python 3.11 rejects backslashes inside f-string expressions.", + item.line, + ) + ) + return True, findings + + +def split_string_token(token_text: str) -> tuple[str, str] | None: + index = 0 + while index < len(token_text) and token_text[index].isalpha(): + index += 1 + prefix = token_text[:index].lower() + if "f" not in prefix: + return None + quote = "" + for candidate in ('"""', "'''", '"', "'"): + if token_text[index:].startswith(candidate): + quote = candidate + break + if not quote or not token_text.endswith(quote): + return None + return prefix, token_text[index + len(quote) : -len(quote)] + + +def scan_legacy_fstring_body(path: Path, root: Path, token_text: str, start_line: int, start_column: int) -> list[dict[str, Any]]: + parsed = split_string_token(token_text) + if not parsed: + return [] + _, body = parsed + findings: list[dict[str, Any]] = [] + expression_depth = 0 + line = start_line + column = start_column + index = 0 + while index < len(body): + char = body[index] + next_char = body[index + 1] if index + 1 < len(body) else "" + if expression_depth == 0: + if char == "{" and next_char == "{": + index += 2 + column += 2 + continue + if char == "{" and next_char != "{": + expression_depth = 1 + else: + if char == "\\": + findings.append( + issue( + path, + root, + line, + column + 1, + "fstring-expression-backslash", + "Python 3.11 rejects backslashes inside f-string expressions.", + token_text, + ) + ) + elif char == "{": + expression_depth += 1 + elif char == "}": + expression_depth = max(0, expression_depth - 1) + if char == "\n": + line += 1 + column = 0 + else: + column += 1 + index += 1 + return findings + + +def fstring_compat_issues(path: Path, root: Path, source: str) -> list[dict[str, Any]]: + handled, findings = scan_modern_fstring_tokens(path, root, source) + if handled: + return findings + try: + tokens = tokenize.generate_tokens(io.StringIO(source).readline) + for item in tokens: + if item.type == token.STRING: + findings.extend(scan_legacy_fstring_body(path, root, item.string, item.start[0], item.start[1])) + except tokenize.TokenError: + return findings + return findings + + +def check_file(path: Path, root: Path) -> dict[str, Any]: + try: + source = path.read_text(encoding="utf-8", errors="replace") + except OSError as exc: + issues = [issue(path, root, 0, 0, "read-error", str(exc))] + else: + issues = compile_issues(path, root, source) + issues.extend(fstring_compat_issues(path, root, source)) + return { + "path": rel_path(path, root), + "ok": not issues, + "issue_count": len(issues), + "issues": issues, + } + + +def build_report(root: Path, raw_paths: list[str], target_python: str, generated_at: str) -> dict[str, Any]: + files = expand_scan_paths(root, raw_paths) + checked = [check_file(path, root) for path in files] + issues = [item for file_report in checked for item in file_report["issues"]] + syntax_error_count = sum(1 for item in issues if item["rule"] == "current-python-syntax") + fstring_count = sum(1 for item in issues if item["rule"] == "fstring-expression-backslash") + return { + "schema_version": "1.0", + "ok": not issues, + "generated_at": generated_at, + "root": rel_path(root, ROOT), + "summary": { + "target_python": target_python, + "file_count": len(checked), + "issue_count": len(issues), + "syntax_error_count": syntax_error_count, + "fstring_311_violation_count": fstring_count, + "decision": "pass" if not issues else "block-python-compat", + }, + "rules": [ + { + "key": "current-python-syntax", + "reason": "Every scanned Python source file must compile under the running interpreter.", + }, + { + "key": "fstring-expression-backslash", + "reason": "Python 3.11 rejects backslashes inside f-string expressions; keep escaping outside the expression.", + }, + ], + "files": checked, + "issues": issues, + "artifacts": { + "json": "reports/python_compatibility.json", + "markdown": "reports/python_compatibility.md", + }, + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Python Compatibility", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- target python: `{summary['target_python']}`", + f"- files scanned: `{summary['file_count']}`", + f"- issues: `{summary['issue_count']}`", + f"- syntax errors: `{summary['syntax_error_count']}`", + f"- f-string 3.11 violations: `{summary['fstring_311_violation_count']}`", + "", + "This report catches Python syntax and compatibility hazards that can pass on a newer local interpreter but fail in the supported CI/runtime interpreter.", + "", + "## Issues", + "", + "| Path | Line | Rule | Message |", + "| --- | ---: | --- | --- |", + ] + if report["issues"]: + for item in report["issues"]: + message = str(item["message"]).replace("|", "\\|") + lines.append(f"| `{item['path']}` | {item['line']} | `{item['rule']}` | {message} |") + else: + lines.append("| `none` | 0 | `none` | none |") + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Check Python source compatibility for supported CI/runtime versions.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--path", action="append", default=[], help="Optional file or directory to scan relative to skill_dir.") + parser.add_argument("--target-python", default="3.11") + parser.add_argument("--output-json", default="reports/python_compatibility.json") + parser.add_argument("--output-md", default="reports/python_compatibility.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + root = Path(args.skill_dir).resolve() + report = build_report(root, args.path, args.target_python, args.generated_at) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = root / output_json + if not output_md.is_absolute(): + output_md = root / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/reference_synthesis_markdown.py b/scripts/reference_synthesis_markdown.py new file mode 100644 index 0000000..ab44b66 --- /dev/null +++ b/scripts/reference_synthesis_markdown.py @@ -0,0 +1,99 @@ +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_reference_synthesis.py to keep reference synthesis modeling separate from Markdown rendering." + + +def render_markdown(summary: dict[str, Any]) -> str: + lines = [ + "# Reference Synthesis", + "", + f"Skill: `{summary['skill_name']}`", + f"- Description: {summary['description']}", + f"- Intent confidence: `{summary['intent_confidence']['score']}/100` (`{summary['intent_confidence']['band']}`)", + "", + "## Live GitHub Benchmarks", + "", + ] + if summary["github_benchmarks"]: + for repo in summary["github_benchmarks"]: + lines.extend( + [ + f"### {repo['name']}", + f"- URL: {repo['url']}", + f"- Stars: `{repo['stars']}`", + ] + ) + for item in repo.get("borrow", []): + lines.append(f"- Borrow: {item}") + lines.append("") + else: + lines.append("- No live GitHub benchmarks are attached yet.") + lines.append("") + + lines.extend(["## Curated World-Class Pattern Tracks", ""]) + for track in summary["source_tracks"]: + lines.extend( + [ + f"### {track['name']}", + f"- Type: `{track['source_type']}`", + f"- Evidence mode: `{track['evidence_mode']}`", + f"- Why relevant: {track['why_relevant']}", + f"- Borrow: {track['borrow']}", + f"- Avoid: {track['avoid']}", + "", + ] + ) + + lines.extend(["## Borrow Now", ""]) + for item in summary["synthesis"]["borrow_now"]: + lines.append(f"- {item}") + + lines.extend(["", "## Avoid Now", ""]) + for item in summary["synthesis"]["avoid_now"]: + lines.append(f"- {item}") + + lines.extend(["", "## Pattern Gate", ""]) + pattern_gate = summary["synthesis"]["pattern_gate"] + lines.append(f"- Summary: {pattern_gate['summary']}") + lines.append(f"- Acceptance threshold: `{pattern_gate['threshold']}/4`") + if pattern_gate["accepted"]: + lines.append("- Accepted patterns:") + for item in pattern_gate["accepted"][:5]: + lines.append( + f" - **{item['name']}**: {item['score']}/4 " + f"({', '.join(item['passed'])})" + ) + if pattern_gate["deferred"]: + lines.append("- Deferred patterns:") + for item in pattern_gate["deferred"][:5]: + lines.append( + f" - **{item['name']}**: missing {', '.join(item['missing']) or 'none'}" + ) + + lines.extend(["", "## Default Recommendation", ""]) + lines.append(f"- Summary: {summary['synthesis']['recommendation']['summary']}") + lines.append(f"- Why: {summary['synthesis']['recommendation']['why']}") + lines.append(f"- User decision required: `{summary['synthesis']['recommendation']['user_decision_required']}`") + + lines.extend(["", "## Visibility Mode", ""]) + lines.append(f"- Mode: `{summary['synthesis']['visibility']['mode']}`") + if summary["synthesis"]["visibility"]["reasons"]: + lines.append(f"- Reasons: {', '.join(summary['synthesis']['visibility']['reasons'])}") + lines.append(f"- User note: {summary['synthesis']['visibility']['user_note']}") + lines.append(f"- Reviewer note: {summary['synthesis']['visibility']['reviewer_note']}") + + lines.extend(["", "## Conflict Check", ""]) + if summary["synthesis"]["conflicts"]: + for conflict in summary["synthesis"]["conflicts"]: + lines.append(f"- **{conflict['key']}**: {conflict['summary']}") + else: + lines.append("- No material design conflict detected. Keep the synthesis silent for the user.") + + lines.extend(["", "## Quality Lift Thesis", ""]) + for item in summary["synthesis"]["quality_risers"]: + lines.append(f"- {item}") + + lines.extend(["", "## Decision Prompt", "", summary["synthesis"]["decision_prompt"], ""]) + return "\n".join(lines).strip() + "\n" diff --git a/scripts/registry_audit.py b/scripts/registry_audit.py new file mode 100644 index 0000000..922a2f6 --- /dev/null +++ b/scripts/registry_audit.py @@ -0,0 +1,370 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from datetime import date +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + +from skill_ir_paths import find_skill_ir as find_skill_ir_document + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_REGISTRY_DIR = ROOT / "registry" +REQUIRED_PACKAGE_FIELDS = [ + "name", + "version", + "description", + "targets", + "maturity", + "owner", + "review_cadence", + "trust_level", + "license", +] + + +def display_path(path: Path, root: Path = ROOT) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +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 read_frontmatter(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + text = path.read_text(encoding="utf-8", errors="replace") + if not text.startswith("---"): + return {} + parts = text.split("---", 2) + if len(parts) < 3: + return {} + if yaml is not None: + payload = yaml.safe_load(parts[1]) or {} + return payload if isinstance(payload, dict) else {} + data: dict[str, Any] = {} + for line in parts[1].splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data + + +def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict[str, Any], str]: + return find_skill_ir_document(skill_dir, name, fallback_source="missing") + + +def license_id(skill_dir: Path) -> str: + for name in ("LICENSE", "LICENSE.md", "LICENSE.txt"): + path = skill_dir / name + if not path.exists(): + continue + text = path.read_text(encoding="utf-8", errors="replace") + if re.search(r"\bMIT License\b", text, re.IGNORECASE): + return "MIT" + for line in text.splitlines(): + value = line.strip() + if value: + return value[:80] + return "" + + +def compatibility_matrix(skill_dir: Path, declared_targets: list[str]) -> dict[str, str]: + conformance = load_json(skill_dir / "reports" / "conformance_matrix.json") + matrix = {} + for target in conformance.get("targets", []): + name = str(target.get("target", "")) + if name: + matrix[name] = str(target.get("status", "unknown")) + if "agent-skills-compatible" in declared_targets and "agent-skills" in matrix: + matrix["agent-skills-compatible"] = matrix["agent-skills"] + for target in declared_targets: + matrix.setdefault(str(target), "missing") + return matrix + + +def package_hash(skill_dir: Path) -> str: + trust = load_json(skill_dir / "reports" / "security_trust_report.json") + return str(trust.get("summary", {}).get("package_sha256", "")) + + +def package_verification(skill_dir: Path) -> dict[str, Any]: + return load_json(skill_dir / "reports" / "package_verification.json") + + +def install_simulation(skill_dir: Path) -> dict[str, Any]: + return load_json(skill_dir / "reports" / "install_simulation.json") + + +def adoption_drift(skill_dir: Path) -> dict[str, Any]: + return load_json(skill_dir / "reports" / "adoption_drift_report.json") + + +def review_waivers(skill_dir: Path) -> dict[str, Any]: + return load_json(skill_dir / "reports" / "review_waivers.json") + + +def review_annotations(skill_dir: Path) -> dict[str, Any]: + return load_json(skill_dir / "reports" / "review_annotations.json") + + +def compiled_targets(skill_dir: Path) -> dict[str, Any]: + return load_json(skill_dir / "reports" / "compiled_targets.json") + + +def package_targets(manifest: dict[str, Any], interface_doc: dict[str, Any], ir: dict[str, Any]) -> list[str]: + candidates = ( + manifest.get("target_platforms") + or ir.get("targets") + or interface_doc.get("compatibility", {}).get("adapter_targets") + or [] + ) + targets = [] + for item in candidates: + value = str(item).strip() + if value and value not in targets: + targets.append(value) + return targets + + +def build_package_metadata(skill_dir: Path, generated_at: str) -> dict[str, Any]: + frontmatter = read_frontmatter(skill_dir / "SKILL.md") + manifest = load_json(skill_dir / "manifest.json") + interface_doc = load_yaml(skill_dir / "agents" / "interface.yaml") + compatibility = interface_doc.get("compatibility", {}) + trust = compatibility.get("trust", {}) + name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name) + ir, ir_source = find_skill_ir(skill_dir, name) + verification = package_verification(skill_dir) + install = install_simulation(skill_dir) + adoption = adoption_drift(skill_dir) + waivers = review_waivers(skill_dir) + annotations = review_annotations(skill_dir) + compiled = compiled_targets(skill_dir) + archive_sha = str(verification.get("summary", {}).get("archive_sha256", "")) + checksums = {"package_sha256": package_hash(skill_dir)} + if archive_sha: + checksums["archive_sha256"] = archive_sha + description = str( + ir.get("trigger_surface", {}).get("description") + or frontmatter.get("description") + or manifest.get("description") + or "" + ) + targets = package_targets(manifest, interface_doc, ir) + package = { + "schema_version": "2.0", + "name": name, + "version": str(manifest.get("version") or frontmatter.get("version") or ""), + "description": description, + "targets": targets, + "maturity": str(manifest.get("maturity_tier") or manifest.get("lifecycle_stage") or "scaffold"), + "owner": str(manifest.get("owner") or ""), + "review_cadence": str(manifest.get("review_cadence") or ""), + "trust_level": str(trust.get("source_tier") or ""), + "license": license_id(skill_dir), + "checksums": checksums, + "compatibility": compatibility_matrix(skill_dir, targets), + "source": { + "skill_dir": display_path(skill_dir), + "skill_ir": ir_source, + "ir_schema_version": str(ir.get("schema_version") or "missing"), + "canonical_metadata": "agents/interface.yaml" if (skill_dir / "agents" / "interface.yaml").exists() else "missing", + }, + "artifacts": { + "overview_html": "reports/skill-overview.html", + "review_studio_html": "reports/review-studio.html", + "trust_report": "reports/security_trust_report.md", + "conformance_matrix": "reports/conformance_matrix.md", + "compiled_targets": "reports/compiled_targets.md" if compiled else "", + "package_verification": "reports/package_verification.md" if verification else "", + "install_simulation": "reports/install_simulation.md" if install else "", + "adoption_drift": "reports/adoption_drift_report.md" if adoption else "", + "review_waivers": "reports/review_waivers.md" if waivers else "", + "review_annotations": "reports/review_annotations.md" if annotations else "", + "package_metadata": f"registry/packages/{name}.json", + }, + "distribution": { + "archive_verified": bool(verification.get("ok")) if verification else False, + "archive_sha256": archive_sha, + "package_verification": "reports/package_verification.json" if verification else "", + "install_simulated": bool(install.get("ok")) if install else False, + "install_simulation": "reports/install_simulation.json" if install else "", + }, + "generated_at": generated_at, + } + return package + + +def audit_package(package: dict[str, Any], skill_dir: Path) -> tuple[list[str], list[str]]: + failures = [] + warnings = [] + for field in REQUIRED_PACKAGE_FIELDS: + if not package.get(field): + failures.append(f"Missing package metadata field: {field}") + if not package.get("checksums", {}).get("package_sha256"): + failures.append("Missing package checksum: checksums.package_sha256") + if package.get("source", {}).get("ir_schema_version") != "2.0.0": + failures.append("Missing valid Skill IR source for registry package") + compatibility = package.get("compatibility", {}) + for target in package.get("targets", []): + if compatibility.get(target) not in {"pass", "warn"}: + failures.append(f"Compatibility is not passing for target: {target}") + for rel in ( + "reports/skill-overview.html", + "reports/review-studio.html", + "reports/compiled_targets.md", + "reports/security_trust_report.md", + "reports/review_waivers.md", + "reports/review_annotations.md", + ): + if not (skill_dir / rel).exists(): + warnings.append(f"Recommended registry evidence is missing: {rel}") + verification = package.get("distribution", {}) + if verification.get("package_verification") and not verification.get("archive_verified"): + failures.append("Package verification report exists but archive verification did not pass") + if verification.get("install_simulation") and not verification.get("install_simulated"): + failures.append("Install simulation report exists but install simulation did not pass") + return failures, warnings + + +def build_index(package: dict[str, Any], generated_at: str) -> dict[str, Any]: + return { + "schema_version": "2.0", + "generated_at": generated_at, + "package_count": 1, + "packages": [ + { + "name": package["name"], + "version": package["version"], + "maturity": package["maturity"], + "owner": package["owner"], + "targets": package["targets"], + "package_metadata": package["artifacts"]["package_metadata"], + "package_sha256": package["checksums"]["package_sha256"], + } + ], + } + + +def render_markdown(report: dict[str, Any]) -> str: + package = report["package"] + lines = [ + "# Registry Audit", + "", + f"- OK: `{report['ok']}`", + f"- Package: `{package['name']}`", + f"- Version: `{package['version']}`", + f"- Maturity: `{package['maturity']}`", + f"- Owner: `{package['owner']}`", + f"- License: `{package['license']}`", + f"- Package SHA256: `{package['checksums']['package_sha256']}`", + f"- Archive SHA256: `{package['checksums'].get('archive_sha256', 'n/a')}`", + f"- Install simulated: `{package.get('distribution', {}).get('install_simulated', False)}`", + "", + "## Compatibility", + "", + "| Target | Status |", + "| --- | --- |", + ] + for target, status in package["compatibility"].items(): + lines.append(f"| `{target}` | `{status}` |") + lines.extend(["", "## Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) + lines.extend(["", "## Warnings", ""]) + lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"]) + lines.extend(["", "## Artifacts", ""]) + for key, value in report["artifacts"].items(): + lines.append(f"- {key}: `{value}`") + return "\n".join(lines) + "\n" + + +def run_registry_audit( + skill_dir: Path, + registry_dir: Path, + output_json: Path, + output_md: Path, + generated_at: str, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + registry_dir = registry_dir.resolve() + package = build_package_metadata(skill_dir, generated_at) + failures, warnings = audit_package(package, skill_dir) + index = build_index(package, generated_at) + + package_dir = registry_dir / "packages" + package_path = package_dir / f"{package['name']}.json" + index_path = registry_dir / "index.json" + package_dir.mkdir(parents=True, exist_ok=True) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + package_path.write_text(json.dumps(package, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + index_path.write_text(json.dumps(index, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + report = { + "ok": not failures, + "schema_version": "2.0", + "skill_dir": display_path(skill_dir), + "registry_dir": display_path(registry_dir), + "package": package, + "index": index, + "failures": failures, + "warnings": warnings, + "artifacts": { + "index": display_path(index_path), + "package": display_path(package_path), + "json": display_path(output_json), + "markdown": display_path(output_md), + }, + } + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Build and audit Skill OS registry package metadata.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--registry-dir", default=str(DEFAULT_REGISTRY_DIR)) + parser.add_argument("--output-json", default="reports/registry_audit.json") + parser.add_argument("--output-md", default="reports/registry_audit.md") + parser.add_argument("--generated-at", default=str(date.today())) + args = parser.parse_args() + + payload = run_registry_audit( + Path(args.skill_dir), + Path(args.registry_dir), + Path(args.output_json), + Path(args.output_md), + args.generated_at, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_adoption_drift_report.py b/scripts/render_adoption_drift_report.py new file mode 100644 index 0000000..c1bf6d4 --- /dev/null +++ b/scripts/render_adoption_drift_report.py @@ -0,0 +1,472 @@ +#!/usr/bin/env python3 +import argparse +import json +from collections import Counter, defaultdict +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent + +ALLOWED_EVENTS = {"skill_activation", "skill_output", "script_run", "review_event"} +ADOPTION_EVENTS = {"skill_activation", "skill_output"} +ALLOWED_ACTIVATION_TYPES = {"implicit", "explicit", "manual", "unknown"} +ALLOWED_OUTCOMES = {"accepted", "edited", "rejected", "missed", "failed", "reviewed", "unknown"} +ALLOWED_FAILURE_TYPES = { + "none", + "wrong_trigger", + "under_trigger", + "bad_output", + "missing_resource", + "script_error", + "review_overdue", +} +ALLOWED_FIELDS = { + "command", + "event", + "skill", + "source", + "version", + "activation_type", + "outcome", + "failure_type", + "timestamp", +} +ALLOWED_SOURCES = {"manual", "yao_cli", "external", "unknown"} +SENSITIVE_FIELDS = { + "prompt", + "content", + "input", + "inputs", + "output", + "outputs", + "transcript", + "message", + "messages", + "note", + "text", + "raw", +} + + +def display_path(path: Path, root: Path = ROOT) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +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 read_frontmatter(path: Path) -> dict[str, str]: + if not path.exists(): + return {} + text = path.read_text(encoding="utf-8", errors="replace") + if not text.startswith("---"): + return {} + parts = text.split("---", 2) + if len(parts) < 3: + return {} + data: dict[str, str] = {} + for line in parts[1].splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data + + +def skill_defaults(skill_dir: Path) -> dict[str, str]: + manifest = load_json(skill_dir / "manifest.json") + frontmatter = read_frontmatter(skill_dir / "SKILL.md") + return { + "skill": str(manifest.get("name") or frontmatter.get("name") or skill_dir.name), + "version": str(manifest.get("version") or frontmatter.get("version") or "0.0.0"), + } + + +def utc_now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def normalize_event(raw: dict[str, Any], defaults: dict[str, str], line_label: str) -> tuple[dict[str, str] | None, list[str]]: + failures: list[str] = [] + raw_keys = set(raw.keys()) + sensitive = sorted(raw_keys & SENSITIVE_FIELDS) + if sensitive: + failures.append(f"{line_label}: raw content fields are not allowed in telemetry: {', '.join(sensitive)}") + unknown = sorted(raw_keys - ALLOWED_FIELDS - SENSITIVE_FIELDS) + if unknown: + failures.append(f"{line_label}: unknown telemetry fields are blocked by the metadata-only contract: {', '.join(unknown)}") + + event = str(raw.get("event") or "skill_activation") + activation_type = str(raw.get("activation_type") or "unknown") + outcome = str(raw.get("outcome") or "unknown") + failure_type = str(raw.get("failure_type") or "none") + timestamp = str(raw.get("timestamp") or utc_now()) + skill = str(raw.get("skill") or defaults["skill"]) + version = str(raw.get("version") or defaults["version"]) + source = str(raw.get("source") or "manual") + command = str(raw.get("command") or "unknown") + + if event not in ALLOWED_EVENTS: + failures.append(f"{line_label}: unsupported event `{event}`") + if activation_type not in ALLOWED_ACTIVATION_TYPES: + failures.append(f"{line_label}: unsupported activation_type `{activation_type}`") + if outcome not in ALLOWED_OUTCOMES: + failures.append(f"{line_label}: unsupported outcome `{outcome}`") + if failure_type not in ALLOWED_FAILURE_TYPES: + failures.append(f"{line_label}: unsupported failure_type `{failure_type}`") + if source not in ALLOWED_SOURCES: + failures.append(f"{line_label}: unsupported source `{source}`") + if not command.replace("-", "").replace("_", "").isalnum() or len(command) > 64: + failures.append(f"{line_label}: command must use only letters, numbers, hyphens, or underscores and stay under 64 chars") + + if failures: + return None, failures + return { + "command": command, + "event": event, + "skill": skill, + "source": source, + "version": version, + "activation_type": activation_type, + "outcome": outcome, + "failure_type": failure_type, + "timestamp": timestamp, + }, [] + + +def load_events(path: Path, defaults: dict[str, str]) -> tuple[list[dict[str, str]], list[str]]: + if not path.exists(): + return [], [] + events: list[dict[str, str]] = [] + failures: list[str] = [] + for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1): + if not line.strip(): + continue + try: + raw = json.loads(line) + except json.JSONDecodeError as exc: + failures.append(f"line {index}: invalid JSONL event: {exc.msg}") + continue + if not isinstance(raw, dict): + failures.append(f"line {index}: telemetry event must be a JSON object") + continue + event, event_failures = normalize_event(raw, defaults, f"line {index}") + failures.extend(event_failures) + if event: + events.append(event) + return events, failures + + +def append_event(path: Path, event: dict[str, str]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a", encoding="utf-8") as handle: + handle.write(json.dumps(event, ensure_ascii=False, sort_keys=True) + "\n") + + +def adoption_by_skill(events: list[dict[str, str]]) -> list[dict[str, Any]]: + grouped: dict[str, Counter[str]] = defaultdict(Counter) + adoption_grouped: dict[str, Counter[str]] = defaultdict(Counter) + for event in events: + skill = event["skill"] + grouped[skill]["events"] += 1 + if event["event"] in ADOPTION_EVENTS: + adoption_grouped[skill][event["outcome"]] += 1 + rows = [] + for skill, counts in sorted(grouped.items()): + adoption_counts = adoption_grouped[skill] + adoption_total = sum(adoption_counts.values()) + adopted = adoption_counts["accepted"] + adoption_counts["edited"] + rows.append( + { + "skill": skill, + "events": counts["events"], + "adoption_events": adoption_total, + "accepted": adoption_counts["accepted"], + "edited": adoption_counts["edited"], + "rejected": adoption_counts["rejected"], + "missed": adoption_counts["missed"], + "adoption_rate": round(adopted / adoption_total * 100, 1) if adoption_total else 0, + } + ) + return rows + + +def atlas_review_overdue(skill_dir: Path) -> int: + atlas = load_json(skill_dir / "reports" / "skill_atlas.json") + stale = atlas.get("stale_skills", []) if isinstance(atlas.get("stale_skills"), list) else [] + return sum( + 1 + for item in stale + if item.get("actionable", True) and "overdue" in str(item.get("reason", "")).lower() + ) + + +def next_candidates(summary: dict[str, Any]) -> list[dict[str, str]]: + candidates: list[dict[str, str]] = [] + if summary["event_count"] == 0: + candidates.append( + { + "signal": "no telemetry", + "recommendation": "Start with a small metadata-only sample before using telemetry for release decisions.", + } + ) + if summary["missed_trigger_count"]: + candidates.append( + { + "signal": "missed trigger", + "recommendation": "Add missed prompts to trigger eval and tighten should-trigger examples.", + } + ) + if summary["wrong_trigger_count"]: + candidates.append( + { + "signal": "wrong trigger", + "recommendation": "Add near-neighbor should-not-trigger cases and clarify exclusions.", + } + ) + if summary["bad_output_count"]: + candidates.append( + { + "signal": "bad output", + "recommendation": "Convert the failed output shape into an Output Eval assertion and failure taxonomy entry.", + } + ) + if summary["script_error_count"]: + candidates.append( + { + "signal": "script error", + "recommendation": "Add non-interactive script smoke tests and clearer error paths.", + } + ) + if summary["review_overdue_count"]: + candidates.append( + { + "signal": "review overdue", + "recommendation": "Prioritize stale governed skills in Skill Atlas before creating more assets.", + } + ) + return candidates[:6] + + +def summarize(events: list[dict[str, str]], review_overdue_count: int) -> dict[str, Any]: + adoption_events = [event for event in events if event["event"] in ADOPTION_EVENTS] + outcomes = Counter(event["outcome"] for event in adoption_events) + failures = Counter(event["failure_type"] for event in events if event["failure_type"] != "none") + event_types = Counter(event["event"] for event in events) + source_types = Counter(event.get("source", "manual") for event in events) + command_counts = Counter(event.get("command", "unknown") for event in events if event.get("command", "unknown") != "unknown") + adopted = outcomes["accepted"] + outcomes["edited"] + event_count = len(events) + adoption_sample_count = len(adoption_events) + missed_trigger = outcomes["missed"] + failures["under_trigger"] + bad_output = failures["bad_output"] + script_error = failures["script_error"] + wrong_trigger = failures["wrong_trigger"] + risk_band = "no-data" + if event_count: + risk_points = missed_trigger + bad_output + script_error + wrong_trigger + review_overdue_count + if risk_points >= 4: + risk_band = "high" + elif risk_points: + risk_band = "medium" + else: + risk_band = "low" + return { + "event_count": event_count, + "adoption_sample_count": adoption_sample_count, + "activation_count": event_types["skill_activation"], + "accepted_count": outcomes["accepted"], + "edited_count": outcomes["edited"], + "rejected_count": outcomes["rejected"], + "missed_count": outcomes["missed"], + "failed_count": outcomes["failed"], + "adoption_rate": round(adopted / adoption_sample_count * 100, 1) if adoption_sample_count else 0, + "missed_trigger_count": missed_trigger, + "wrong_trigger_count": wrong_trigger, + "bad_output_count": bad_output, + "script_error_count": script_error, + "missing_resource_count": failures["missing_resource"], + "review_overdue_count": review_overdue_count + failures["review_overdue"], + "risk_band": risk_band, + "event_types": dict(sorted(event_types.items())), + "failure_types": dict(sorted(failures.items())), + "source_types": dict(sorted(source_types.items())), + "command_counts": dict(sorted(command_counts.items())), + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Adoption And Drift Report", + "", + "Local-first, metadata-only telemetry for skill operations. Raw prompts, outputs, transcripts, and notes are not allowed in the event stream.", + "", + "## Summary", + "", + f"- Events: `{summary['event_count']}`", + f"- Adoption samples: `{summary['adoption_sample_count']}`", + f"- Activation events: `{summary['activation_count']}`", + f"- Adoption rate: `{summary['adoption_rate']}`", + f"- Missed trigger signals: `{summary['missed_trigger_count']}`", + f"- Bad output signals: `{summary['bad_output_count']}`", + f"- Script error signals: `{summary['script_error_count']}`", + f"- Review overdue signals: `{summary['review_overdue_count']}`", + f"- Risk band: `{summary['risk_band']}`", + "", + "## Privacy Contract", + "", + "- Storage is local-first.", + "- Events are metadata-only.", + "- Raw user prompts, model outputs, transcripts, notes, and messages are blocked.", + "- Distributed packages should include this aggregate report, not raw `reports/telemetry_events.jsonl`.", + "", + "## Adoption By Skill", + "", + "| Skill | Events | Adoption Samples | Accepted | Edited | Rejected | Missed | Adoption Rate |", + "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |", + ] + for row in report["adoption_by_skill"]: + lines.append( + f"| `{row['skill']}` | {row['events']} | {row['adoption_events']} | {row['accepted']} | {row['edited']} | " + f"{row['rejected']} | {row['missed']} | {row['adoption_rate']} |" + ) + if not report["adoption_by_skill"]: + lines.append("| `none` | 0 | 0 | 0 | 0 | 0 | 0 | 0 |") + lines.extend(["", "## Next Iteration Candidates", ""]) + for item in report["next_iteration_candidates"]: + lines.append(f"- `{item['signal']}`: {item['recommendation']}") + if not report["next_iteration_candidates"]: + lines.append("- No telemetry-driven iteration candidate yet.") + lines.extend(["", "## Recent Metadata Events", ""]) + for event in report["recent_events"]: + lines.append( + f"- `{event['timestamp']}` `{event['skill']}` event=`{event['event']}` " + f"source=`{event.get('source', 'manual')}` command=`{event.get('command', 'unknown')}` " + f"activation=`{event['activation_type']}` outcome=`{event['outcome']}` failure=`{event['failure_type']}`" + ) + if not report["recent_events"]: + lines.append("- No metadata events captured yet.") + if report["failures"]: + lines.extend(["", "## Validation Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]]) + return "\n".join(lines) + "\n" + + +def render_report( + skill_dir: Path, + events_jsonl: Path | None = None, + output_json: Path | None = None, + output_md: Path | None = None, + generated_at: str | None = None, + record_event: dict[str, Any] | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + defaults = skill_defaults(skill_dir) + events_jsonl = events_jsonl or reports_dir / "telemetry_events.jsonl" + output_json = output_json or reports_dir / "adoption_drift_report.json" + output_md = output_md or reports_dir / "adoption_drift_report.md" + generated_at = generated_at or utc_now() + failures: list[str] = [] + + if record_event is not None: + event, event_failures = normalize_event(record_event, defaults, "record") + failures.extend(event_failures) + if event: + append_event(events_jsonl, event) + + events, load_failures = load_events(events_jsonl, defaults) + failures.extend(load_failures) + summary = summarize(events, atlas_review_overdue(skill_dir)) + report = { + "ok": not failures, + "schema_version": "2.0", + "generated_at": generated_at, + "skill_dir": display_path(skill_dir), + "privacy_contract": { + "storage": "local-first", + "event_scope": "metadata-only", + "raw_content_allowed": False, + "raw_event_log_packaged": False, + "blocked_fields": sorted(SENSITIVE_FIELDS), + }, + "summary": summary, + "adoption_by_skill": adoption_by_skill(events), + "next_iteration_candidates": next_candidates(summary), + "recent_events": events[-10:], + "failures": failures, + "artifacts": { + "events_jsonl": display_path(events_jsonl), + "json": display_path(output_json), + "markdown": display_path(output_md), + }, + } + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render local-first adoption and drift telemetry for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--events-jsonl") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--generated-at") + parser.add_argument("--record-event", choices=sorted(ALLOWED_EVENTS)) + parser.add_argument("--activation-type", choices=sorted(ALLOWED_ACTIVATION_TYPES), default="unknown") + parser.add_argument("--outcome", choices=sorted(ALLOWED_OUTCOMES), default="unknown") + parser.add_argument("--failure-type", choices=sorted(ALLOWED_FAILURE_TYPES), default="none") + parser.add_argument("--source", choices=sorted(ALLOWED_SOURCES), default="manual") + parser.add_argument("--command", default="unknown") + parser.add_argument("--timestamp") + parser.add_argument("--skill-name") + parser.add_argument("--version") + args = parser.parse_args() + + record_event = None + if args.record_event: + record_event = { + "event": args.record_event, + "activation_type": args.activation_type, + "outcome": args.outcome, + "failure_type": args.failure_type, + "source": args.source, + "command": args.command, + } + if args.timestamp: + record_event["timestamp"] = args.timestamp + if args.skill_name: + record_event["skill"] = args.skill_name + if args.version: + record_event["version"] = args.version + + report = render_report( + Path(args.skill_dir), + events_jsonl=Path(args.events_jsonl).resolve() if args.events_jsonl else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + output_md=Path(args.output_md).resolve() if args.output_md else None, + generated_at=args.generated_at, + record_event=record_event, + ) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_architecture_maintainability.py b/scripts/render_architecture_maintainability.py new file mode 100644 index 0000000..d2cc0a7 --- /dev/null +++ b/scripts/render_architecture_maintainability.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +"""Render a maintainability audit for the skill code surface.""" + +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent + + +EXCLUDED_PARTS = { + ".git", + ".mypy_cache", + ".pytest_cache", + "__pycache__", + "dist", + ".previews", +} + + +def iter_python_files(skill_dir: Path) -> list[Path]: + roots = [skill_dir / "scripts", skill_dir / "tests"] + files: list[Path] = [] + for root in roots: + if not root.exists(): + continue + for path in root.rglob("*.py"): + rel_parts = path.relative_to(skill_dir).parts + if any(part in EXCLUDED_PARTS for part in rel_parts): + continue + if len(rel_parts) >= 2 and rel_parts[0] == "tests" and rel_parts[1].startswith("tmp"): + continue + files.append(path) + return sorted(files) + + +def rel(skill_dir: Path, path: Path) -> str: + try: + return str(path.resolve().relative_to(skill_dir.resolve())) + except ValueError: + return str(path) + + +def classify_python_file(path: Path, text: str) -> str: + if 'SCRIPT_INTERFACE = "internal-module"' in text or "SCRIPT_INTERFACE = 'internal-module'" in text: + return "internal-module" + if "argparse.ArgumentParser" in text or ".add_argument(" in text or "argparse" in text: + return "cli-script" + if path.parts[-2:-1] == ("tests",) or "/tests/" in path.as_posix(): + return "test" + return "module" + + +def recommendation_for(path: str) -> str: + if path == "scripts/yao.py": + return "Split command handlers by domain while keeping scripts/yao.py as the thin CLI orchestrator." + if path == "scripts/render_review_studio.py": + return "Move data loading and large section renderers into focused review_studio_* modules." + if path == "scripts/render_review_viewer.py": + return "Split viewer data assembly from HTML section rendering." + if path.startswith("tests/"): + return "Break broad integration assertions into focused verifier helpers when the next behavior change lands." + return "Watch this file before adding new responsibilities; extract a helper module when one concern dominates." + + +def count_handlers_in_file(path: Path) -> int: + if not path.exists(): + return 0 + return sum(1 for line in path.read_text(encoding="utf-8", errors="replace").splitlines() if line.startswith("def command_")) + + +def command_module_paths(skill_dir: Path) -> list[Path]: + scripts_dir = skill_dir / "scripts" + if not scripts_dir.exists(): + return [] + paths = [scripts_dir / "yao.py"] + paths.extend(sorted(scripts_dir.glob("yao_cli_*commands.py"))) + return [path for path in paths if path.exists()] + + +def count_cli_command_handlers(skill_dir: Path) -> int: + return sum(count_handlers_in_file(path) for path in command_module_paths(skill_dir)) + + +def build_report(skill_dir: Path, warn_lines: int, block_lines: int, trend_lines: int, generated_at: str) -> dict[str, Any]: + files = iter_python_files(skill_dir) + watch_lines = max(1, int(warn_lines * 0.8)) + early_watch_lines = max(1, min(trend_lines, watch_lines)) + records: list[dict[str, Any]] = [] + internal_count = 0 + cli_count = 0 + test_count = 0 + script_count = 0 + for path in files: + text = path.read_text(encoding="utf-8", errors="replace") + line_count = len(text.splitlines()) + kind = classify_python_file(path, text) + rel_path = rel(skill_dir, path) + if kind == "internal-module": + internal_count += 1 + if kind == "cli-script": + cli_count += 1 + if rel_path.startswith("tests/"): + test_count += 1 + if rel_path.startswith("scripts/"): + script_count += 1 + severity = "pass" + if line_count >= block_lines: + severity = "block" + elif line_count >= warn_lines: + severity = "warn" + early_watch = severity == "pass" and line_count >= early_watch_lines + records.append( + { + "path": rel_path, + "lines": line_count, + "kind": kind, + "severity": severity, + "early_watch": early_watch, + "recommendation": recommendation_for(rel_path), + } + ) + records.sort(key=lambda item: (-int(item["lines"]), str(item["path"]))) + hotspots = [item for item in records if item["severity"] in {"warn", "block"}] + watchlist = [item for item in records if item["severity"] == "pass" and int(item["lines"]) >= watch_lines] + early_watchlist = [ + item + for item in records + if item["severity"] == "pass" and int(item["lines"]) >= early_watch_lines and item not in watchlist + ] + blockers = [item for item in records if item["severity"] == "block"] + summary = { + "python_file_count": len(records), + "script_file_count": script_count, + "test_file_count": test_count, + "internal_module_count": internal_count, + "cli_script_count": cli_count, + "command_handler_count": count_cli_command_handlers(skill_dir), + "entrypoint_command_handler_count": count_handlers_in_file(skill_dir / "scripts" / "yao.py"), + "command_module_count": len(command_module_paths(skill_dir)), + "warn_line_threshold": warn_lines, + "watch_line_threshold": watch_lines, + "early_watch_line_threshold": early_watch_lines, + "block_line_threshold": block_lines, + "largest_file_lines": records[0]["lines"] if records else 0, + "watchlist_count": len(watchlist), + "early_watchlist_count": len(early_watchlist), + "hotspot_count": len(hotspots), + "blocker_count": len(blockers), + "decision": "block-maintainability" if blockers else ("watch-maintainability-hotspots" if hotspots else "pass"), + } + return { + "schema_version": "1.0", + "ok": not blockers, + "generated_at": generated_at, + "skill_dir": ".", + "summary": summary, + "largest_files": records[:12], + "watchlist": watchlist[:12], + "early_watchlist": early_watchlist[:12], + "hotspots": hotspots, + "actions": [ + { + "path": item["path"], + "severity": item["severity"], + "action": item["recommendation"], + } + for item in hotspots[:8] + ], + "artifacts": { + "json": "reports/architecture_maintainability.json", + "markdown": "reports/architecture_maintainability.md", + }, + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Architecture Maintainability", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- python files: `{summary['python_file_count']}`", + f"- scripts: `{summary['script_file_count']}`", + f"- tests: `{summary['test_file_count']}`", + f"- internal modules: `{summary['internal_module_count']}`", + f"- CLI scripts: `{summary['cli_script_count']}`", + f"- Yao CLI command handlers: `{summary['command_handler_count']}`", + f"- entrypoint command handlers: `{summary['entrypoint_command_handler_count']}`", + f"- command modules: `{summary['command_module_count']}`", + f"- largest file lines: `{summary['largest_file_lines']}`", + f"- early watch threshold lines: `{summary['early_watch_line_threshold']}`", + f"- early watchlist: `{summary['early_watchlist_count']}`", + f"- watch threshold lines: `{summary['watch_line_threshold']}`", + f"- watchlist: `{summary['watchlist_count']}`", + f"- hotspots: `{summary['hotspot_count']}`", + f"- blockers: `{summary['blocker_count']}`", + "", + "This report keeps maintainability risk visible before the Meta Skill grows more gates, renderers, and CLI commands.", + "", + "## Hotspots", + "", + ] + hotspots = report.get("hotspots", []) + if hotspots: + lines.extend(["| File | Lines | Kind | Severity | Recommended action |", "| --- | ---: | --- | --- | --- |"]) + for item in hotspots: + lines.append( + f"| `{item['path']}` | `{item['lines']}` | `{item['kind']}` | `{item['severity']}` | {item['recommendation']} |" + ) + else: + lines.append("No file-size hotspots found.") + lines.extend(["", "## Watchlist", ""]) + watchlist = report.get("watchlist", []) + if watchlist: + lines.extend(["| File | Lines | Kind | Recommended next split |", "| --- | ---: | --- | --- |"]) + for item in watchlist: + lines.append(f"| `{item['path']}` | `{item['lines']}` | `{item['kind']}` | {item['recommendation']} |") + else: + lines.append("No near-threshold files found.") + lines.extend(["", "## Early Watchlist", ""]) + early_watchlist = report.get("early_watchlist", []) + if early_watchlist: + lines.extend(["| File | Lines | Kind | Recommended next split |", "| --- | ---: | --- | --- |"]) + for item in early_watchlist: + lines.append(f"| `{item['path']}` | `{item['lines']}` | `{item['kind']}` | {item['recommendation']} |") + else: + lines.append("No early watch files found.") + lines.extend(["", "## Largest Files", ""]) + if report.get("largest_files"): + lines.extend(["| File | Lines | Kind | Severity |", "| --- | ---: | --- | --- |"]) + for item in report["largest_files"]: + lines.append(f"| `{item['path']}` | `{item['lines']}` | `{item['kind']}` | `{item['severity']}` |") + else: + lines.append("No Python files found under scripts/ or tests/.") + lines.extend( + [ + "", + "## Release Rule", + "", + "- `block` hotspots should be split before governed release.", + "- `warn` hotspots can ship only when Review Studio keeps them visible and a reviewer accepts the modularization plan.", + "- Do not split a file only for line count; split when a stable responsibility boundary is clear.", + ] + ) + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render architecture maintainability evidence for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--warn-lines", type=int, default=900) + parser.add_argument("--block-lines", type=int, default=1500) + parser.add_argument("--trend-lines", type=int, default=600) + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = build_report(skill_dir, args.warn_lines, args.block_lines, args.trend_lines, args.generated_at) + output_json = Path(args.output_json) if args.output_json else skill_dir / "reports" / "architecture_maintainability.json" + output_md = Path(args.output_md) if args.output_md else skill_dir / "reports" / "architecture_maintainability.md" + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_artifact_design_profile.py b/scripts/render_artifact_design_profile.py new file mode 100644 index 0000000..17aac11 --- /dev/null +++ b/scripts/render_artifact_design_profile.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +ARTIFACT_FAMILIES = [ + { + "key": "tutorial", + "label": "Tutorial or guide", + "keywords": ["tutorial", "guide", "lesson", "course", "walkthrough", "how to", "教程", "指南", "课程"], + "direction": "Progressive instructional layout with domain-specific section names, short success checks, and examples close to the user's real input.", + "layout": ["opening promise", "task-specific sections", "worked example", "success checks", "common mistakes"], + "quality_gates": [ + "Replace generic headings with learner- and domain-specific headings.", + "Pair every major step with a visible success check.", + "Do not add screenshots unless they are real, current, and action-relevant.", + ], + }, + { + "key": "report", + "label": "Report or brief", + "keywords": ["report", "brief", "analysis", "summary", "memo", "white paper", "报告", "简报", "分析", "总结"], + "direction": "High-trust editorial report with a clear first-screen thesis, compact evidence blocks, and decisions separated from supporting detail.", + "layout": ["thesis", "evidence blocks", "decision table", "risks", "next actions"], + "quality_gates": [ + "Keep the first screen useful without requiring the reader to parse every detail.", + "Use tables only for comparisons; move explanations below the table.", + "Keep source notes readable without flooding the body with markers.", + ], + }, + { + "key": "review_viewer", + "label": "Review viewer", + "keywords": ["review", "viewer", "compare", "diff", "audit", "评审", "对比", "审查"], + "direction": "Side-by-side reviewer studio with explicit tradeoffs, evidence readiness, and fast paths for approving, blocking, or requesting one focused fix.", + "layout": ["summary", "variant comparison", "evidence", "risks", "review decision"], + "quality_gates": [ + "Make differences visible instead of hiding them in prose.", + "Separate author-facing recommendations from reviewer-only evidence.", + "Surface conflicts clearly and keep routine benchmark synthesis quiet.", + ], + }, + { + "key": "dashboard", + "label": "Dashboard or metrics page", + "keywords": ["dashboard", "metric", "score", "kpi", "chart", "table", "scorecard", "仪表盘", "指标", "评分", "图表", "表格"], + "direction": "Metric-first dashboard with stable dimensions, short labels, visible deltas, and narrative callouts only where they change interpretation.", + "layout": ["metric board", "ranked signals", "comparison rows", "interpretation", "action queue"], + "quality_gates": [ + "Avoid paragraph-heavy table cells.", + "Keep charts tied to one analytical question each.", + "Preserve stable color meaning across metrics and entities.", + ], + }, + { + "key": "visual_capture", + "label": "Screenshot or visual evidence", + "keywords": ["screenshot", "screen capture", "image evidence", "figma", "截图", "图片", "截屏", "视觉证据"], + "direction": "Evidence-led visual artifact that records source, viewport, crop intent, and the exact region the reader should inspect.", + "layout": ["source context", "visual frame", "inspection notes", "limitations", "next capture"], + "quality_gates": [ + "Never invent missing screenshots or visual states.", + "Record source, viewport, and crop intent.", + "Describe the action-relevant region instead of only saying an image exists.", + ], + }, + { + "key": "slide_like", + "label": "Slide-like narrative", + "keywords": ["slide", "ppt", "deck", "presentation", "one-pager", "演示", "幻灯片", "一页纸"], + "direction": "Narrative page rhythm with strong section roles, controlled density, and a visual system chosen from the topic rather than a default template.", + "layout": ["hero claim", "supporting proof", "structured contrast", "implication", "closing action"], + "quality_gates": [ + "Plan role, density, and rhythm before writing HTML.", + "Avoid three or more repeated surfaces in a row.", + "Use structure, spacing, and type before decoration.", + ], + }, + { + "key": "code_or_cli", + "label": "Code, CLI, or implementation guide", + "keywords": ["code", "cli", "script", "api", "command", "terminal", "代码", "脚本", "命令", "接口"], + "direction": "Execution-focused technical artifact with environment assumptions, copyable commands, expected outputs, and side effects made explicit.", + "layout": ["prerequisites", "commands", "expected output", "failure handling", "rollback or cleanup"], + "quality_gates": [ + "Name the working directory and required inputs for commands.", + "Mark destructive, networked, or external side-effect operations.", + "Prefer the smallest runnable snippet over broad framework scaffolding.", + ], + }, +] + + +DESIGN_TOKENS = { + "type": [ + "Use a distinctive display face or serif for major claims when the artifact is editorial.", + "Use a restrained sans for dense body text and technical details.", + "Use mono only for metadata, paths, commands, labels, and evidence tags.", + ], + "color": [ + "Choose colors from the artifact's domain, brand, or evidence mood.", + "Do not default to Kami parchment, purple gradients, or generic SaaS blue unless the content justifies it.", + "Keep accent color limited to decisions, active states, risk, or section anchors.", + ], + "spacing": [ + "Prefer clear grid rhythm over floating decorative cards.", + "Increase whitespace around decisions and shrink whitespace around supporting metadata.", + "Split dense content instead of shrinking type or adding scroll traps.", + ], + "components": [ + "Use cards for grouped evidence, tables for comparisons, callouts for decisions, and timelines for sequence.", + "Avoid cards inside cards.", + "Keep reviewer-only detail visible but visually quieter than user-facing guidance.", + ], +} + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def load_json(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def normalized_text(skill_dir: Path) -> str: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + intent = load_json(skill_dir / "reports" / "intent-confidence.json") + output_risk = load_json(skill_dir / "reports" / "output-risk-profile.json") + context = intent.get("context", {}) if isinstance(intent, dict) else {} + risk_labels = " ".join(item.get("label", "") for item in output_risk.get("risk_families", [])) + parts = [ + skill_dir.name, + str(frontmatter.get("name", "")), + str(frontmatter.get("description", "")), + body, + str(context.get("job", "")), + str(context.get("primary_output", "")), + " ".join(context.get("real_inputs", []) or []), + " ".join(context.get("constraints", []) or []), + " ".join(context.get("standards", []) or []), + risk_labels, + ] + return " ".join(parts).lower() + + +def match_family(text: str, family: dict) -> tuple[int, list[str]]: + hits = [] + for keyword in family["keywords"]: + if keyword.lower() in text: + hits.append(keyword) + return len(hits), hits + + +def dedupe(items: list[str], limit: int) -> list[str]: + seen = set() + output = [] + for item in items: + if not item or item in seen: + continue + seen.add(item) + output.append(item) + if len(output) == limit: + break + return output + + +def design_system_name(matched: list[dict]) -> str: + keys = {item["key"] for item in matched} + if "dashboard" in keys: + return "metric editorial" + if "review_viewer" in keys: + return "review studio" + if "slide_like" in keys: + return "narrative visual brief" + if "tutorial" in keys: + return "guided learning document" + if "visual_capture" in keys: + return "evidence frame" + return "content-led editorial" + + +def build_summary(skill_dir: Path) -> dict: + text = normalized_text(skill_dir) + matched = [] + for family in ARTIFACT_FAMILIES: + score, hits = match_family(text, family) + if score: + matched.append({**family, "score": score, "matched_keywords": hits}) + if not matched: + fallback = next(item for item in ARTIFACT_FAMILIES if item["key"] == "report") + matched = [{**fallback, "score": 0, "matched_keywords": ["general-artifact"]}] + matched = sorted(matched, key=lambda item: item["score"], reverse=True)[:5] + primary = matched[0] + layout_patterns = [] + gates = [] + for item in matched: + layout_patterns.extend(item["layout"]) + gates.extend(item["quality_gates"]) + anti_patterns = [ + "Do not copy Kami's fixed parchment background as a default.", + "Do not use generic purple gradients, glass cards, or stock SaaS hero sections unless the content calls for them.", + "Do not let Markdown tables become the default shape for every comparison or explanation.", + "Do not turn reviewer evidence into user-facing clutter.", + "Do not invent screenshots, citations, charts, or UI states.", + ] + return { + "skill_name": skill_dir.name, + "design_system": design_system_name(matched), + "primary_artifact": { + "key": primary["key"], + "label": primary["label"], + "direction": primary["direction"], + "matched_keywords": primary["matched_keywords"], + }, + "artifact_families": [ + { + "key": item["key"], + "label": item["label"], + "score": item["score"], + "matched_keywords": item["matched_keywords"], + "direction": item["direction"], + } + for item in matched + ], + "layout_patterns": dedupe(layout_patterns, 8), + "design_tokens": DESIGN_TOKENS, + "quality_gates": dedupe(gates, 8), + "anti_patterns": anti_patterns, + "reviewer_note": "Use this profile to judge whether the generated artifacts feel designed for their job, not merely rendered.", + } + + +def render_markdown(summary: dict) -> str: + lines = [ + "# Artifact Design Profile", + "", + f"Skill: `{summary['skill_name']}`", + f"Design system: `{summary['design_system']}`", + "", + "## Primary Artifact Direction", + "", + f"**{summary['primary_artifact']['label']}**", + "", + summary["primary_artifact"]["direction"], + "", + "## Matched Artifact Families", + "", + ] + for family in summary["artifact_families"]: + lines.extend( + [ + f"### {family['label']}", + f"- Matched keywords: {', '.join(family['matched_keywords'])}", + f"- Score: `{family['score']}`", + f"- Direction: {family['direction']}", + "", + ] + ) + lines.extend(["## Layout Patterns To Prefer", ""]) + for item in summary["layout_patterns"]: + lines.append(f"- {item}") + lines.extend(["", "## Design Tokens", ""]) + for key, values in summary["design_tokens"].items(): + lines.append(f"### {key.title()}") + for value in values: + lines.append(f"- {value}") + lines.append("") + lines.extend(["## Quality Gates", ""]) + for item in summary["quality_gates"]: + lines.append(f"- {item}") + lines.extend(["", "## Anti-Patterns", ""]) + for item in summary["anti_patterns"]: + lines.append(f"- {item}") + lines.extend(["", "## Reviewer Note", "", summary["reviewer_note"], ""]) + return "\n".join(lines).strip() + "\n" + + +def render_artifact_design_profile( + skill_dir: Path, + output_md: Path | None = None, + output_json: Path | None = None, +) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "artifact-design-profile.md" + output_json = output_json or reports_dir / "artifact-design-profile.json" + summary = build_summary(skill_dir) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render artifact design direction and visual quality gates for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + result = render_artifact_design_profile( + Path(args.skill_dir), + Path(args.output_md).resolve() if args.output_md else None, + Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_baseline_compare.py b/scripts/render_baseline_compare.py new file mode 100644 index 0000000..a055daf --- /dev/null +++ b/scripts/render_baseline_compare.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +def parse_entry(value: str) -> tuple[str, Path]: + if "::" not in value: + raise ValueError("Entry must use label::path format.") + label, raw_path = value.split("::", 1) + return label.strip() or "target", Path(raw_path).resolve() + + +def load_report(path: Path) -> dict: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"Unexpected payload in {path}") + return payload + + +def metric_snapshot(report: dict, prefix: str) -> dict: + summary = report.get("summary", {}) + return { + "tokens": summary.get(f"{prefix}_tokens", 0), + "dev_errors": summary.get(f"{prefix}_dev_total_errors", 0), + "holdout_errors": summary.get(f"{prefix}_holdout_total_errors", 0), + "blind_errors": summary.get(f"{prefix}_blind_holdout_total_errors", 0), + "judge_blind_errors": summary.get(f"{prefix}_judge_blind_holdout_total_errors", 0), + "adversarial_errors": summary.get(f"{prefix}_adversarial_holdout_total_errors", 0), + } + + +def total_errors(snapshot: dict) -> int: + return ( + snapshot["dev_errors"] + + snapshot["holdout_errors"] + + snapshot["blind_errors"] + + snapshot["judge_blind_errors"] + + snapshot["adversarial_errors"] + ) + + +def compare_target(label: str, report: dict) -> dict: + baseline = metric_snapshot(report, "baseline") + current = metric_snapshot(report, "current") + winner = metric_snapshot(report, "winner") + return { + "label": label, + "winner_label": report.get("summary", {}).get("winner_label", report.get("winner", {}).get("label", "winner")), + "baseline": baseline, + "current": current, + "winner": winner, + "delta": { + "current_vs_baseline": total_errors(baseline) - total_errors(current), + "winner_vs_baseline": total_errors(baseline) - total_errors(winner), + "winner_vs_current": total_errors(current) - total_errors(winner), + }, + } + + +def build_summary(comparisons: list[dict]) -> dict: + baseline_total = sum(total_errors(item["baseline"]) for item in comparisons) + current_total = sum(total_errors(item["current"]) for item in comparisons) + winner_total = sum(total_errors(item["winner"]) for item in comparisons) + return { + "target_count": len(comparisons), + "baseline_total_errors": baseline_total, + "current_total_errors": current_total, + "winner_total_errors": winner_total, + "winner_vs_baseline_gain": baseline_total - winner_total, + "winner_vs_current_gain": current_total - winner_total, + } + + +def render_markdown(payload: dict) -> str: + lines = [ + "# Baseline Compare", + "", + "A lightweight with-skill vs baseline comparison across tracked optimization targets.", + "", + f"- Targets: `{payload['summary']['target_count']}`", + f"- Baseline total errors: `{payload['summary']['baseline_total_errors']}`", + f"- Current total errors: `{payload['summary']['current_total_errors']}`", + f"- Winner total errors: `{payload['summary']['winner_total_errors']}`", + f"- Winner vs baseline gain: `{payload['summary']['winner_vs_baseline_gain']}`", + f"- Winner vs current gain: `{payload['summary']['winner_vs_current_gain']}`", + "", + "## Target Breakdown", + "", + "| Target | Baseline Errors | Current Errors | Winner Errors | Winner Label |", + "| --- | ---: | ---: | ---: | --- |", + ] + for item in payload["comparisons"]: + lines.append( + f"| {item['label']} | {total_errors(item['baseline'])} | {total_errors(item['current'])} | {total_errors(item['winner'])} | {item['winner_label']} |" + ) + return "\n".join(lines).strip() + "\n" + + +def render_baseline_compare(entries: list[tuple[str, Path]], output_json: Path | None = None, output_md: Path | None = None) -> dict: + comparisons = [compare_target(label, load_report(path)) for label, path in entries] + payload = { + "summary": build_summary(comparisons), + "comparisons": comparisons, + } + if output_json: + output_json.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + if output_md: + output_md.parent.mkdir(parents=True, exist_ok=True) + output_md.write_text(render_markdown(payload), encoding="utf-8") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a baseline comparison report from description optimization artifacts.") + parser.add_argument("--entry", action="append", required=True, help="Format: label::/absolute/path/to/description_optimization.json") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + args = parser.parse_args() + entries = [parse_entry(value) for value in args.entry] + payload = render_baseline_compare( + entries, + output_json=Path(args.output_json).resolve() if args.output_json else None, + output_md=Path(args.output_md).resolve() if args.output_md else None, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_benchmark_reproducibility.py b/scripts/render_benchmark_reproducibility.py new file mode 100644 index 0000000..d422c6d --- /dev/null +++ b/scripts/render_benchmark_reproducibility.py @@ -0,0 +1,685 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import subprocess +from datetime import date +from pathlib import Path +from typing import Any + +from benchmark_release_lock import git_status, release_lock_status + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders a release-facing benchmark reproducibility manifest and Markdown report." + +METHODOLOGY_SECTIONS = [ + "## Benchmark Types", + "## Sample Sources", + "## Evaluation Dimensions", + "## Weighting Rule", + "## Failure Disclosure", + "## Reproduction", +] + +REQUIRED_ARTIFACTS = [ + ("methodology", "reports/benchmark_methodology.md"), + ("failure_disclosure", "evals/failure-cases.md"), + ("output_cases", "evals/output/cases.jsonl"), + ("output_schema", "evals/output/schema.json"), + ("output_scorecard", "reports/output_quality_scorecard.json"), + ("output_execution", "reports/output_execution_runs.json"), + ("blind_review", "reports/output_blind_review_pack.json"), + ("review_adjudication", "reports/output_review_adjudication.json"), + ("trigger_scorecard", "reports/route_scorecard.json"), + ("runtime_conformance", "reports/conformance_matrix.json"), + ("trust_report", "reports/security_trust_report.json"), + ("python_compatibility", "reports/python_compatibility.json"), + ("registry_audit", "reports/registry_audit.json"), + ("package_verification", "reports/package_verification.json"), + ("install_simulation", "reports/install_simulation.json"), + ("skill_os2_audit", "reports/skill_os2_audit.json"), + ("world_class_evidence_plan", "reports/world_class_evidence_plan.json"), + ("world_class_evidence_ledger", "reports/world_class_evidence_ledger.json"), + ("world_class_evidence_intake", "reports/world_class_evidence_intake.json"), + ("world_class_evidence_preflight", "reports/world_class_evidence_preflight.json"), + ("world_class_submission_review", "reports/world_class_submission_review.json"), + ("world_class_operator_runbook", "reports/world_class_operator_runbook.json"), + ("world_class_operator_runbook_markdown", "reports/world_class_operator_runbook.md"), + ("world_class_operator_runbook_html", "reports/world_class_operator_runbook.html"), + ("world_class_claim_guard", "reports/world_class_claim_guard.json"), +] + +REPRODUCTION_COMMANDS = [ + { + "label": "source commit", + "command": "git rev-parse HEAD", + "evidence": "git commit hash", + }, + { + "label": "trigger eval", + "command": "make eval-suite", + "evidence": "reports/eval_suite.json", + }, + { + "label": "output eval", + "command": "python3 scripts/yao.py output-eval", + "evidence": "reports/output_quality_scorecard.json", + }, + { + "label": "output execution", + "command": "python3 scripts/yao.py output-exec --runner-command '[\"python3\",\"scripts/local_output_eval_runner.py\"]'", + "evidence": "reports/output_execution_runs.json", + }, + { + "label": "blind review adjudication", + "command": "python3 scripts/yao.py output-review", + "evidence": "reports/output_review_adjudication.json", + }, + { + "label": "skill ir", + "command": "python3 scripts/yao.py skill-ir . --output-json skill-ir/examples/yao-meta-skill.json", + "evidence": "skill-ir/examples/yao-meta-skill.json", + }, + { + "label": "runtime conformance", + "command": "python3 scripts/yao.py conformance .", + "evidence": "reports/conformance_matrix.json", + }, + { + "label": "trust report", + "command": "python3 scripts/yao.py trust .", + "evidence": "reports/security_trust_report.json", + }, + { + "label": "python compatibility", + "command": "python3 scripts/yao.py python-compat .", + "evidence": "reports/python_compatibility.json", + }, + { + "label": "package", + "command": "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --expectations evals/packaging_expectations.json --output-dir dist --zip", + "evidence": "dist/yao-meta-skill.zip", + }, + { + "label": "package verify", + "command": "python3 scripts/yao.py package-verify . --package-dir dist --require-zip", + "evidence": "reports/package_verification.json", + }, + { + "label": "install simulate", + "command": "python3 scripts/yao.py install-simulate . --package-dir dist", + "evidence": "reports/install_simulation.json", + }, + { + "label": "registry audit", + "command": "python3 scripts/yao.py registry-audit .", + "evidence": "reports/registry_audit.json", + }, + { + "label": "skill os audit", + "command": "python3 scripts/yao.py skill-os2-audit .", + "evidence": "reports/skill_os2_audit.json", + }, + { + "label": "world-class evidence plan", + "command": "python3 scripts/yao.py world-class-evidence .", + "evidence": "reports/world_class_evidence_plan.json", + }, + { + "label": "world-class evidence ledger", + "command": "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_ledger.json", + }, + { + "label": "world-class evidence intake", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_intake.json", + }, + { + "label": "world-class evidence preflight", + "command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_evidence_preflight.json", + }, + { + "label": "world-class submission review", + "command": "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_submission_review.json", + }, + { + "label": "world-class operator runbook", + "command": "python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions", + "evidence": "reports/world_class_operator_runbook.json", + }, + { + "label": "world-class claim guard", + "command": "python3 scripts/yao.py world-class-claim-guard .", + "evidence": "reports/world_class_claim_guard.json", + }, + { + "label": "evidence consistency", + "command": "python3 scripts/yao.py evidence-consistency .", + "evidence": "reports/evidence_consistency.json", + }, + { + "label": "full ci", + "command": "make ci-test", + "evidence": "CI target output", + }, +] + + +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 rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git_commit(skill_dir: Path) -> str: + try: + proc = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=skill_dir, + capture_output=True, + text=True, + check=True, + ) + except (OSError, subprocess.CalledProcessError): + return "unknown" + return proc.stdout.strip() or "unknown" + + +def count_jsonl(path: Path) -> int: + if not path.exists(): + return 0 + return sum(1 for line in path.read_text(encoding="utf-8").splitlines() if line.strip()) + + +def count_failure_cases(path: Path) -> int: + if not path.exists(): + return 0 + return sum(1 for line in path.read_text(encoding="utf-8").splitlines() if line.startswith("### ")) + + +def methodology_check(path: Path) -> dict[str, Any]: + text = path.read_text(encoding="utf-8") if path.exists() else "" + sections = [{"heading": heading, "exists": heading in text} for heading in METHODOLOGY_SECTIONS] + return { + "path": "reports/benchmark_methodology.md", + "exists": path.exists(), + "sections": sections, + "missing_sections": [item["heading"] for item in sections if not item["exists"]], + } + + +def artifact_record(skill_dir: Path, label: str, rel: str) -> dict[str, Any]: + path = skill_dir / rel + record: dict[str, Any] = { + "label": label, + "path": rel, + "exists": path.exists(), + } + if path.exists() and path.is_file(): + record["bytes"] = path.stat().st_size + record["sha256"] = sha256_file(path) + return record + + +def evidence_bundle_fingerprint(artifacts: list[dict[str, Any]]) -> dict[str, Any]: + digest = hashlib.sha256() + existing_count = 0 + missing_paths = [] + for artifact in sorted(artifacts, key=lambda item: str(item.get("path", ""))): + path = str(artifact.get("path", "")) + digest.update(path.encode("utf-8")) + digest.update(b"\0") + digest.update(str(artifact.get("label", "")).encode("utf-8")) + digest.update(b"\0") + digest.update(str(bool(artifact.get("exists"))).encode("utf-8")) + digest.update(b"\0") + digest.update(str(artifact.get("sha256", "")).encode("utf-8")) + digest.update(b"\0") + if artifact.get("exists"): + existing_count += 1 + else: + missing_paths.append(path) + return { + "algorithm": "sha256(path,label,exists,artifact_sha256)", + "artifact_count": len(artifacts), + "existing_count": existing_count, + "missing_count": len(missing_paths), + "missing_paths": missing_paths, + "sha256": digest.hexdigest(), + } + + +def public_claim_blockers( + local_reproducibility_ready: bool, + release_lock_ready: bool, + provider_evidence_complete: bool, + human_review_complete: bool, + world_class_ready: bool, + world_class_open_gap_count: int, + world_class_ledger_pending_count: int, + world_class_source_check_count: int, + world_class_source_pass_count: int, + world_class_source_blocked_count: int, +) -> list[str]: + blockers = [] + if not local_reproducibility_ready: + blockers.append("local benchmark reproducibility is incomplete") + if not release_lock_ready: + blockers.append("release lock is not clean or commit is unavailable") + if not provider_evidence_complete: + blockers.append("provider-backed model holdout evidence is incomplete") + if not human_review_complete: + blockers.append("human blind-review adjudication is incomplete") + if not world_class_ready: + blockers.append( + f"world-class evidence is not accepted yet ({world_class_open_gap_count} open gaps, " + f"{world_class_ledger_pending_count} ledger pending)" + ) + if ( + world_class_source_check_count == 0 + or world_class_source_pass_count != world_class_source_check_count + or world_class_source_blocked_count > 0 + ): + blockers.append( + "world-class source checks are not all accepted " + f"({world_class_source_pass_count}/{world_class_source_check_count} pass, " + f"{world_class_source_blocked_count} blocked)" + ) + return blockers + + +def beta_test_blockers( + local_reproducibility_ready: bool, + release_lock_ready: bool, + provider_evidence_complete: bool, +) -> list[str]: + blockers = [] + if not local_reproducibility_ready: + blockers.append("local benchmark reproducibility is incomplete") + if not release_lock_ready: + blockers.append("release lock is not clean or commit is unavailable") + if not provider_evidence_complete: + blockers.append("provider-backed model holdout source evidence is incomplete") + return blockers + + +BETA_DEFERRED_EVIDENCE = { + "provider-holdout": { + "label": "Provider holdout ledger review", + "reason": "Provider-backed source evidence exists, but formal ledger submission and reviewer acceptance are still pending before public claims.", + }, + "human-adjudication": { + "label": "Human blind-review adjudication", + "reason": "Human adjudication evidence is still pending; deferred for beta/public testing and still required before superiority, fully-reviewed, or world-class claims.", + }, + "native-permission-enforcement": { + "label": "Native permission enforcement evidence", + "reason": "Native enforcement proof is still pending; deferred for beta/public testing and still required before world-class claims.", + }, + "native-client-telemetry": { + "label": "Real client telemetry evidence", + "reason": "Real client telemetry is still pending; deferred for beta/public testing and still required before world-class claims.", + }, +} + + +def beta_deferred_evidence(world_class_ledger: dict[str, Any]) -> list[dict[str, str]]: + deferred = [] + entries = world_class_ledger.get("entries", []) + if not isinstance(entries, list): + return deferred + for entry in entries: + if not isinstance(entry, dict) or entry.get("status") != "pending": + continue + key = str(entry.get("key") or "") + if not key: + continue + fallback = BETA_DEFERRED_EVIDENCE.get(key, {}) + deferred.append( + { + "key": key, + "label": str(entry.get("label") or fallback.get("label") or key), + "reason": str( + fallback.get("reason") + or "Formal evidence is still pending; deferred for beta/public testing and still required before public claims." + ), + } + ) + return deferred + + +def build_report(skill_dir: Path, generated_at: str) -> dict[str, Any]: + reports = skill_dir / "reports" + output_quality = load_json(reports / "output_quality_scorecard.json") + output_execution = load_json(reports / "output_execution_runs.json") + output_review = load_json(reports / "output_review_adjudication.json") + skill_os2 = load_json(reports / "skill_os2_audit.json") + world_class_plan = load_json(reports / "world_class_evidence_plan.json") + world_class_ledger = load_json(reports / "world_class_evidence_ledger.json") + trust = load_json(reports / "security_trust_report.json") + package_verification = load_json(reports / "package_verification.json") + methodology = methodology_check(reports / "benchmark_methodology.md") + artifacts = [artifact_record(skill_dir, label, rel) for label, rel in REQUIRED_ARTIFACTS] + evidence_bundle = evidence_bundle_fingerprint(artifacts) + missing_artifacts = [item["path"] for item in artifacts if not item["exists"]] + output_summary = output_quality.get("summary", {}) + execution_summary = output_execution.get("summary", {}) + review_summary = output_review.get("summary", {}) + failure_case_count = count_failure_cases(skill_dir / "evals" / "failure-cases.md") + output_case_count = count_jsonl(skill_dir / "evals" / "output" / "cases.jsonl") + status = git_status(skill_dir) + commit = git_commit(skill_dir) + release_lock = release_lock_status(status, commit) + local_reproducibility_ready = ( + not methodology["missing_sections"] + and not missing_artifacts + and output_case_count >= 5 + and failure_case_count > 0 + and output_summary.get("gate_pass") is True + and execution_summary.get("command_executed_count", 0) > 0 + and execution_summary.get("timing_observed_count", 0) > 0 + ) + human_review_complete = review_summary.get("pair_count", 0) > 0 and review_summary.get("pending_count", 0) == 0 + provider_evidence_complete = execution_summary.get("model_executed_count", 0) > 0 and execution_summary.get("token_observed_count", 0) > 0 + world_class_ready = bool(skill_os2.get("summary", {}).get("world_class_ready", False)) + world_class_open_gap_count = int(skill_os2.get("summary", {}).get("open_gap_count", 0) or 0) + world_class_summary = world_class_ledger.get("summary", {}) + world_class_ledger_pending_count = int(world_class_summary.get("pending_count", 0) or 0) + world_class_source_check_count = int(world_class_summary.get("source_check_count", 0) or 0) + world_class_source_pass_count = int(world_class_summary.get("source_pass_count", 0) or 0) + world_class_source_blocked_count = int(world_class_summary.get("source_blocked_count", 0) or 0) + claim_blockers = public_claim_blockers( + local_reproducibility_ready, + release_lock["ready"], + provider_evidence_complete, + human_review_complete, + world_class_ready, + world_class_open_gap_count, + world_class_ledger_pending_count, + world_class_source_check_count, + world_class_source_pass_count, + world_class_source_blocked_count, + ) + public_claim_ready = not claim_blockers + beta_blockers = beta_test_blockers( + local_reproducibility_ready, + release_lock["ready"], + provider_evidence_complete, + ) + beta_deferred = beta_deferred_evidence(world_class_ledger) + beta_test_ready = not beta_blockers + limitations = [ + "The git commit and dirty flags are generation-time context; release lock is blocked by source changes, while generated evidence artifacts are tracked separately.", + "Pending blind-review decisions are visible but do not count as human adjudication.", + "World-class readiness remains false until external and human evidence gaps close.", + "Beta/public testing may proceed without human blind-review only when wording avoids superiority, fully-reviewed, or world-class claims.", + ] + if provider_evidence_complete: + limitations.insert( + 1, + "Provider-backed model holdout source evidence is complete, but ledger acceptance still requires a valid independently reviewed submission packet.", + ) + else: + limitations.insert( + 1, + "Local command-runner evidence is reproducible but does not replace provider-backed model holdout evidence.", + ) + return { + "schema_version": "1.0", + "ok": local_reproducibility_ready, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "commit": commit, + "git_status": status, + "summary": { + "reproducibility_ready": local_reproducibility_ready, + "release_lock_ready": release_lock["ready"], + "methodology_complete": not methodology["missing_sections"], + "required_artifact_count": len(artifacts), + "missing_artifact_count": len(missing_artifacts), + "evidence_bundle_sha256": evidence_bundle["sha256"], + "source_contract_sha256": trust.get("summary", {}).get("package_sha256", ""), + "archive_sha256": package_verification.get("summary", {}).get("archive_sha256", ""), + "output_case_count": output_case_count, + "failure_disclosure_count": failure_case_count, + "command_count": len(REPRODUCTION_COMMANDS), + "command_executed_count": execution_summary.get("command_executed_count", 0), + "timing_observed_count": execution_summary.get("timing_observed_count", 0), + "model_executed_count": execution_summary.get("model_executed_count", 0), + "token_observed_count": execution_summary.get("token_observed_count", 0), + "human_review_complete": human_review_complete, + "provider_evidence_complete": provider_evidence_complete, + "world_class_ready": world_class_ready, + "world_class_open_gap_count": world_class_open_gap_count, + "world_class_task_count": world_class_plan.get("summary", {}).get("task_count", 0), + "world_class_ledger_pending_count": world_class_ledger_pending_count, + "world_class_source_check_count": world_class_source_check_count, + "world_class_source_pass_count": world_class_source_pass_count, + "world_class_source_blocked_count": world_class_source_blocked_count, + "beta_test_ready": beta_test_ready, + "beta_test_blocker_count": len(beta_blockers), + "beta_test_deferred_evidence_count": len(beta_deferred), + "public_claim_ready": public_claim_ready, + "public_claim_blocker_count": len(claim_blockers), + "working_tree_dirty": status.get("dirty"), + "changed_file_count": status.get("changed_file_count"), + "source_tree_dirty": status.get("source_dirty"), + "source_changed_file_count": status.get("source_changed_file_count"), + "generated_tree_dirty": status.get("generated_dirty"), + "generated_changed_file_count": status.get("generated_changed_file_count"), + }, + "beta_test_release": { + "ready": beta_test_ready, + "scope": "beta/public test release without superiority, fully-reviewed, or world-class claims", + "blockers": beta_blockers, + "allowed_deferred_evidence": beta_deferred, + "policy": "Human blind-review, native permission enforcement, real client telemetry, and ledger acceptance may be deferred for beta/public testing, but public claims must remain blocked until those evidence entries are accepted.", + "required_wording": "Use beta, public test, or technical preview wording; do not claim world-class readiness, fully reviewed quality, or proven superiority over baseline.", + }, + "public_claim": { + "ready": public_claim_ready, + "scope": "public benchmark or world-class readiness claim", + "blockers": claim_blockers, + "policy": "Local reproducibility can pass before public claims; public claims require provider evidence, human adjudication, clean release lock, accepted world-class evidence, and complete source checks.", + }, + "release_lock": release_lock, + "evidence_bundle": evidence_bundle, + "methodology": methodology, + "artifacts_checked": artifacts, + "missing_artifacts": missing_artifacts, + "reproduction_commands": REPRODUCTION_COMMANDS, + "failure_disclosure": { + "path": "evals/failure-cases.md", + "case_count": failure_case_count, + "policy": "Keep representative failures visible and tied to regression checks.", + }, + "limitations": limitations, + "artifacts": { + "json": "reports/benchmark_reproducibility.json", + "markdown": "reports/benchmark_reproducibility.md", + }, + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Benchmark Reproducibility", + "", + f"Generated at: `{report['generated_at']}`", + f"Commit: `{report['commit']}`", + f"Working tree dirty at generation: `{str(summary.get('working_tree_dirty')).lower()}`", + f"Source tree dirty at generation: `{str(summary.get('source_tree_dirty')).lower()}`", + f"Generated evidence dirty at generation: `{str(summary.get('generated_tree_dirty')).lower()}`", + f"Evidence bundle SHA256: `{summary.get('evidence_bundle_sha256', '')}`", + "", + "## Summary", + "", + f"- reproducibility ready: `{str(summary['reproducibility_ready']).lower()}`", + f"- release lock ready: `{str(summary.get('release_lock_ready')).lower()}`", + f"- methodology complete: `{str(summary['methodology_complete']).lower()}`", + f"- required artifacts: `{summary['required_artifact_count']}`", + f"- missing artifacts: `{summary['missing_artifact_count']}`", + f"- source contract sha256: `{summary.get('source_contract_sha256', '')[:12]}`", + f"- archive sha256: `{summary.get('archive_sha256', '')[:12]}`", + f"- output cases: `{summary['output_case_count']}`", + f"- disclosed failure cases: `{summary['failure_disclosure_count']}`", + f"- reproduction commands: `{summary['command_count']}`", + f"- provider evidence complete: `{str(summary['provider_evidence_complete']).lower()}`", + f"- human review complete: `{str(summary['human_review_complete']).lower()}`", + f"- world-class ready: `{str(summary['world_class_ready']).lower()}`", + f"- world-class source checks: `{summary.get('world_class_source_pass_count', 0)}` pass / `{summary.get('world_class_source_check_count', 0)}` total; `{summary.get('world_class_source_blocked_count', 0)}` blocked", + f"- beta test ready: `{str(summary['beta_test_ready']).lower()}`", + f"- beta test blockers: `{summary['beta_test_blocker_count']}`", + f"- beta deferred evidence: `{summary['beta_test_deferred_evidence_count']}`", + f"- public claim ready: `{str(summary['public_claim_ready']).lower()}`", + f"- public claim blockers: `{summary['public_claim_blocker_count']}`", + f"- changed files at generation: `{summary.get('changed_file_count')}`", + f"- source changed files at generation: `{summary.get('source_changed_file_count')}`", + f"- generated changed files at generation: `{summary.get('generated_changed_file_count')}`", + "", + "This report proves local benchmark reproducibility only. It keeps external provider and human-review gaps visible instead of counting them as complete. The git commit and dirty samples are generation-time context; the evidence bundle SHA is the durable anchor for the artifacts listed below.", + "", + ] + beta_release = report.get("beta_test_release", {}) + lines.extend( + [ + "## Beta Test Boundary", + "", + f"- ready: `{str(beta_release.get('ready')).lower()}`", + f"- scope: {beta_release.get('scope', 'beta/public test release')}", + f"- policy: {beta_release.get('policy', '')}", + f"- required wording: {beta_release.get('required_wording', '')}", + "", + "| Blocker |", + "| --- |", + ] + ) + beta_blockers = beta_release.get("blockers", []) + if beta_blockers: + lines.extend(f"| {item} |" for item in beta_blockers) + else: + lines.append("| none |") + lines.extend(["", "| Deferred evidence | Reason |", "| --- | --- |"]) + deferred = beta_release.get("allowed_deferred_evidence", []) + if deferred: + lines.extend(f"| `{item.get('key', '')}` | {item.get('reason', '')} |" for item in deferred) + else: + lines.append("| none | none |") + lines.extend( + [ + "", + "## Public Claim Boundary", + "", + ] + ) + lines.extend( + [ + f"- ready: `{str(report.get('public_claim', {}).get('ready')).lower()}`", + f"- scope: {report.get('public_claim', {}).get('scope', 'public benchmark claim')}", + f"- policy: {report.get('public_claim', {}).get('policy', '')}", + "", + "| Blocker |", + "| --- |", + ] + ) + blockers = report.get("public_claim", {}).get("blockers", []) + if blockers: + lines.extend(f"| {item} |" for item in blockers) + else: + lines.append("| none |") + lines.extend( + [ + "", + "## Release Lock", + "", + f"- ready: `{str(report.get('release_lock', {}).get('ready')).lower()}`", + f"- reason: {report.get('release_lock', {}).get('reason', 'unknown')}", + f"- status scope: {report.get('release_lock', {}).get('status_scope', 'generation-time status')}", + "", + "## Evidence Bundle", + ] + ) + lines.extend( + [ + "", + f"- algorithm: `{report.get('evidence_bundle', {}).get('algorithm', '')}`", + f"- artifacts: `{report.get('evidence_bundle', {}).get('existing_count', 0)}` / `{report.get('evidence_bundle', {}).get('artifact_count', 0)}`", + f"- sha256: `{report.get('evidence_bundle', {}).get('sha256', '')}`", + "", + "## Methodology Sections", + "", + "| Section | Status |", + "| --- | --- |", + ] + ) + for section in report["methodology"]["sections"]: + lines.append(f"| `{section['heading']}` | {'present' if section['exists'] else 'missing'} |") + lines.extend(["", "## Required Artifacts", "", "| Label | Path | Status | SHA256 |", "| --- | --- | --- | --- |"]) + for artifact in report["artifacts_checked"]: + digest = artifact.get("sha256", "") + lines.append( + f"| {artifact['label']} | `{artifact['path']}` | {'present' if artifact['exists'] else 'missing'} | `{digest[:12]}` |" + ) + lines.extend(["", "## Reproduction Commands", ""]) + for command in report["reproduction_commands"]: + lines.append(f"- `{command['command']}`") + lines.append(f" - evidence: `{command['evidence']}`") + lines.extend(["", "## Failure Disclosure", ""]) + disclosure = report["failure_disclosure"] + lines.append(f"- path: `{disclosure['path']}`") + lines.append(f"- disclosed cases: `{disclosure['case_count']}`") + lines.append(f"- policy: {disclosure['policy']}") + lines.extend(["", "## Limits", ""]) + lines.extend(f"- {item}" for item in report["limitations"]) + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render benchmark reproducibility evidence.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json", default="reports/benchmark_reproducibility.json") + parser.add_argument("--output-md", default="reports/benchmark_reproducibility.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = build_report(skill_dir, args.generated_at) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_context_reports.py b/scripts/render_context_reports.py new file mode 100644 index 0000000..a7f6e43 --- /dev/null +++ b/scripts/render_context_reports.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from resource_boundary_check import analyze_skill + + +ROOT = Path(__file__).resolve().parent.parent +TARGETS = [ + { + "label": "root", + "path": ROOT, + "json_output": ROOT / "reports" / "context_budget.json", + }, + { + "label": "complex-release-orchestrator", + "path": ROOT / "examples" / "complex-release-orchestrator" / "generated-skill", + "json_output": ROOT / "examples" / "complex-release-orchestrator" / "generated-skill" / "reports" / "context_budget.json", + }, + { + "label": "governed-incident-command", + "path": ROOT / "examples" / "governed-incident-command" / "generated-skill", + "json_output": ROOT / "examples" / "governed-incident-command" / "generated-skill" / "reports" / "context_budget.json", + }, +] + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Render context budget reports for root and example skills.") + parser.add_argument( + "--generated-at", + default="2026-03-31", + help="Report date written to context_budget_summary.json.", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + rows = [] + for target in TARGETS: + report = analyze_skill(target["path"]) + target["json_output"].parent.mkdir(parents=True, exist_ok=True) + target["json_output"].write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + stats = report.get("stats", {}) + rows.append( + { + "label": target["label"], + "path": str(target["path"].relative_to(ROOT)) if target["path"] != ROOT else ".", + "budget_tier": stats.get("context_budget_tier"), + "budget_limit": stats.get("context_budget_limit"), + "initial_tokens": stats.get("estimated_initial_load_tokens"), + "skill_body_tokens": stats.get("skill_body_tokens"), + "deferred_resource_tokens": stats.get("deferred_resource_tokens"), + "large_deferred_resource_dirs": stats.get("large_deferred_resource_dirs", []), + "deferred_resource_governance": stats.get("deferred_resource_governance", {}), + "quality_density": stats.get("quality_density"), + "unused_resource_dirs": stats.get("unused_resource_dirs", []), + "ok": report.get("ok"), + "warnings": report.get("warnings", []), + } + ) + + summary = { + "generated_at": args.generated_at, + "targets": rows, + } + (ROOT / "reports").mkdir(parents=True, exist_ok=True) + (ROOT / "reports" / "context_budget_summary.json").write_text( + json.dumps(summary, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + + lines = [ + "# Context Budget Summary", + "", + "| Target | Path | Tier | Limit | Initial | SKILL | Deferred | Resource Governance | Large Deferred Dirs | Quality Density | Unused Dirs | Status |", + "| --- | --- | --- | ---: | ---: | ---: | ---: | --- | --- | ---: | --- | --- |", + ] + for row in rows: + unused = ", ".join(row["unused_resource_dirs"]) if row["unused_resource_dirs"] else "-" + governance = row.get("deferred_resource_governance", {}) or {} + governance_status = governance.get("status", "unknown") + large_dirs = ", ".join( + f"{item['path']}:{item['estimated_tokens']}" + for item in row.get("large_deferred_resource_dirs", []) + ) or "-" + status = "ok" if row["ok"] else "fail" + lines.append( + f"| {row['label']} | `{row['path']}` | `{row['budget_tier']}` | {row['budget_limit']} | {row['initial_tokens']} | {row['skill_body_tokens']} | {row['deferred_resource_tokens']} | `{governance_status}` | {large_dirs} | {row['quality_density']} | {unused} | {status} |" + ) + lines.extend(["", "Per-target JSON reports are written beside each target and under `reports/`."]) + (ROOT / "reports" / "context_budget.md").write_text("\n".join(lines) + "\n", encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/scripts/render_daily_skillops_report.py b/scripts/render_daily_skillops_report.py new file mode 100644 index 0000000..77b913c --- /dev/null +++ b/scripts/render_daily_skillops_report.py @@ -0,0 +1,484 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date, datetime, timezone +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Any + +from propose_adaptation import build_report as build_proposal_report +from propose_adaptation import render_markdown as render_proposals_markdown +from skillops_opportunity import build_opportunities, decision_policy_contract, summarize_opportunities +from summarize_user_signals import build_report as build_pattern_report +from summarize_user_signals import render_markdown as render_patterns_markdown + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders a Daily SkillOps report that summarizes explicit-source patterns, proposals, approval state, and release evidence without scanning private logs or applying patches." + +SUMMARY_FIELDS = [ + "decision", + "source_supplied", + "pattern_count", + "proposal_count", + "approval_count", + "pending_review_count", + "applied_count", + "rollback_count", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_pending_count", + "release_lock_ready", + "evidence_consistency_ok", + "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 report_date(generated_at: str) -> str: + if len(generated_at) >= 10: + candidate = generated_at[:10] + try: + date.fromisoformat(candidate) + return candidate + except ValueError: + pass + return date.today().isoformat() + + +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_daily_path(skill_dir: Path, generated_at: str, suffix: str) -> Path: + return skill_dir / "reports" / "skillops" / "daily" / f"{report_date(generated_at)}.{suffix}" + + +def source_summary(patterns: dict[str, Any]) -> dict[str, Any]: + source = patterns.get("source", {}) if isinstance(patterns.get("source"), dict) else {} + privacy = patterns.get("privacy_contract", {}) if isinstance(patterns.get("privacy_contract"), dict) else {} + return { + "path": source.get("path", ""), + "fingerprint_sha256": source.get("fingerprint_sha256", ""), + "record_count": source.get("record_count", 0), + "explicit_source": privacy.get("explicit_source_required") is True, + "raw_content_stored": privacy.get("raw_content_stored") is True, + "redacted_excerpts_only": privacy.get("redacted_excerpts_only") is True, + } + + +def compact_proposals(proposals: dict[str, Any]) -> list[dict[str, Any]]: + rows = [] + for item in proposals.get("proposals", []): + if not isinstance(item, dict): + continue + rows.append( + { + "proposal_id": item.get("proposal_id", ""), + "pattern_id": item.get("pattern_id", ""), + "title": item.get("title", ""), + "risk_level": item.get("risk_level", ""), + "status": item.get("status", ""), + "requires_approval": item.get("requires_approval") is True, + "target_files": item.get("target_files", []), + "verification_commands": item.get("verification_commands", []), + } + ) + return rows + + +def build_actions(summary: dict[str, Any], proposal_rows: list[dict[str, Any]]) -> list[dict[str, str]]: + actions: list[dict[str, str]] = [] + if summary["source_supplied"] is False and summary["pattern_count"] == 0: + actions.append( + { + "key": "provide-explicit-source", + "priority": "medium", + "action": "Run daily-skillops with --source pointing to a curated JSONL or Markdown conversation summary.", + } + ) + if proposal_rows: + actions.append( + { + "key": "review-adaptation-proposals", + "priority": "high", + "action": "Review proposal-only adaptation items before preparing any approval ledger entry.", + } + ) + if summary["pending_review_count"]: + actions.append( + { + "key": "resolve-pending-approvals", + "priority": "high", + "action": "Approve, reject, or expire pending adaptive approval ledger entries before applying patches.", + } + ) + if summary["world_class_pending_count"]: + actions.append( + { + "key": "close-world-class-evidence", + "priority": "high", + "action": "Collect accepted external or human evidence for the pending world-class ledger entries.", + } + ) + if summary["evidence_consistency_ok"] is not True: + actions.append( + { + "key": "refresh-evidence-consistency", + "priority": "high", + "action": "Regenerate evidence-consistency before using this report for release decisions.", + } + ) + if not actions: + actions.append( + { + "key": "monitor", + "priority": "low", + "action": "No action required beyond routine daily monitoring.", + } + ) + return actions + + +def build_report( + skill_dir: Path, + generated_at: str, + source: Path | None = None, + min_support: int = 2, + allow_history_source: bool = False, + patterns_json: Path | None = None, + proposals_json: Path | None = None, + write_source_reports: bool = True, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + reports = skill_dir / "reports" + patterns_path = resolve_output(skill_dir, str(patterns_json or "reports/user_patterns.json")) + proposals_path = resolve_output(skill_dir, str(proposals_json or "reports/adaptation_proposals.json")) + failures: list[str] = [] + + if source is not None: + pattern_report = build_pattern_report( + skill_dir, + source, + min_support=max(2, min_support), + generated_at=generated_at, + allow_history_source=allow_history_source, + ) + if pattern_report.get("ok") is True and write_source_reports: + pattern_report["artifacts"] = { + "json": display_path(patterns_path, skill_dir), + "markdown": display_path(patterns_path.with_suffix(".md"), skill_dir), + } + write_json(patterns_path, pattern_report) + write_text(patterns_path.with_suffix(".md"), render_patterns_markdown(pattern_report)) + else: + failures.extend(pattern_report.get("failures", [])) + else: + pattern_report = load_json(patterns_path) + + if pattern_report and pattern_report.get("ok") is True: + proposal_source_path = patterns_path + with TemporaryDirectory() as temp_dir: + if source is not None and not write_source_reports: + proposal_source_path = Path(temp_dir) / "user_patterns.json" + write_json(proposal_source_path, pattern_report) + proposal_report = build_proposal_report(skill_dir, proposal_source_path, generated_at) + if proposal_report.get("ok") is True and write_source_reports: + proposal_report["artifacts"] = { + "json": display_path(proposals_path, skill_dir), + "markdown": display_path(proposals_path.with_suffix(".md"), skill_dir), + } + write_json(proposals_path, proposal_report) + write_text(proposals_path.with_suffix(".md"), render_proposals_markdown(proposal_report)) + elif proposal_report.get("ok") is not True: + failures.extend(proposal_report.get("failures", [])) + else: + proposal_report = load_json(proposals_path) + if source is not None and not pattern_report: + failures.append("Pattern report could not be built from the explicit source.") + + approval = load_json(reports / "adaptation_approval_ledger.json") + regression = load_json(reports / "adaptation_regression_report.json") + coverage = load_json(reports / "skill_os2_coverage.json") + ledger = load_json(reports / "world_class_evidence_ledger.json") + consistency = load_json(reports / "evidence_consistency.json") + benchmark = load_json(reports / "benchmark_reproducibility.json") + + pattern_summary = pattern_report.get("summary", {}) if isinstance(pattern_report.get("summary"), dict) else {} + proposal_summary = proposal_report.get("summary", {}) if isinstance(proposal_report.get("summary"), dict) else {} + approval_summary = approval.get("summary", {}) if isinstance(approval.get("summary"), dict) else {} + regression_summary = regression.get("summary", {}) if isinstance(regression.get("summary"), dict) else {} + coverage_summary = coverage.get("summary", {}) if isinstance(coverage.get("summary"), dict) else {} + ledger_summary = ledger.get("summary", {}) if isinstance(ledger.get("summary"), dict) else {} + benchmark_summary = benchmark.get("summary", {}) if isinstance(benchmark.get("summary"), dict) else {} + proposal_contract = proposal_report.get("proposal_contract", {}) if isinstance(proposal_report.get("proposal_contract"), dict) else {} + + proposal_rows = compact_proposals(proposal_report) + pattern_rows = pattern_report.get("patterns", []) if isinstance(pattern_report.get("patterns"), list) else [] + opportunities = build_opportunities(pattern_rows, proposal_rows) + opportunity_summary = summarize_opportunities(opportunities) + failure_count = len(failures) + summary = { + "decision": "blocked" if failure_count else "proposal-review" if proposal_rows else "monitor", + "source_supplied": source is not None, + "pattern_count": int(pattern_summary.get("pattern_count", 0) or 0), + "proposal_count": int(proposal_summary.get("proposal_count", 0) or len(proposal_rows)), + "approval_count": int(approval_summary.get("approval_count", 0) or 0), + "pending_review_count": int(approval_summary.get("pending_review_count", 0) or 0), + "applied_count": int(approval_summary.get("applied_count", 0) or regression_summary.get("applied_count", 0) or 0), + "rollback_count": int(approval_summary.get("rollback_count", 0) or regression_summary.get("rollback_count", 0) or 0), + "local_blueprint_ready": coverage_summary.get("local_blueprint_ready") is True, + "public_world_class_ready": coverage_summary.get("public_world_class_ready") is True, + "world_class_pending_count": int(ledger_summary.get("pending_count", coverage_summary.get("world_class_evidence_pending_count", 0)) or 0), + "release_lock_ready": benchmark_summary.get("release_lock_ready") is True, + "evidence_consistency_ok": consistency.get("ok") is True, + "writes_source_files": False, + "auto_patch_enabled": False, + "failure_count": failure_count, + } + actions = build_actions(summary, proposal_rows) + operations_contract = { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "explicit_source_required_for_scan": True, + "implicit_private_log_scan": False, + "raw_content_stored": False, + "redacted_excerpts_only": True, + "proposal_only": proposal_contract.get("proposal_only") is not False, + "approval_required_for_writes": True, + "writes_source_files": False, + "auto_patch_enabled": False, + "daily_report_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, + "operations_contract": operations_contract, + "report_contract": { + "schema_version": "1.0", + "contract": "daily-skillops-report", + "top_level_mirrors_summary": True, + "summary_fields": SUMMARY_FIELDS, + "source_of_truth": ["summary", "operations_contract"], + }, + "source": source_summary(pattern_report) if pattern_report else {}, + "patterns": pattern_report.get("patterns", []) if isinstance(pattern_report.get("patterns"), list) else [], + "proposals": proposal_rows, + "opportunity_summary": opportunity_summary, + "opportunities": opportunities, + "decision_policy": decision_policy_contract(), + "approval": { + "approval_count": summary["approval_count"], + "pending_review_count": summary["pending_review_count"], + "applied_count": summary["applied_count"], + "rollback_count": summary["rollback_count"], + }, + "release_state": { + "local_blueprint_ready": summary["local_blueprint_ready"], + "public_world_class_ready": summary["public_world_class_ready"], + "world_class_pending_count": summary["world_class_pending_count"], + "release_lock_ready": summary["release_lock_ready"], + "evidence_consistency_ok": summary["evidence_consistency_ok"], + }, + "actions": actions, + "failures": failures, + "source_reports": { + "patterns": display_path(patterns_path, skill_dir), + "proposals": display_path(proposals_path, skill_dir), + "approval_ledger": "reports/adaptation_approval_ledger.json", + "regression": "reports/adaptation_regression_report.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "world_class_ledger": "reports/world_class_evidence_ledger.json", + "evidence_consistency": "reports/evidence_consistency.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json", + }, + "artifacts": { + "json": str(default_daily_path(skill_dir, generated_at, "json").relative_to(skill_dir)), + "markdown": str(default_daily_path(skill_dir, generated_at, "md").relative_to(skill_dir)), + }, + } + return report + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Daily SkillOps Report", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- source supplied: `{str(summary['source_supplied']).lower()}`", + f"- patterns: `{summary['pattern_count']}`", + f"- proposals: `{summary['proposal_count']}`", + f"- pending approvals: `{summary['pending_review_count']}`", + f"- applied patches: `{summary['applied_count']}`", + f"- rollbacks: `{summary['rollback_count']}`", + f"- local blueprint ready: `{str(summary['local_blueprint_ready']).lower()}`", + f"- public world-class ready: `{str(summary['public_world_class_ready']).lower()}`", + f"- world-class pending: `{summary['world_class_pending_count']}`", + f"- release lock ready: `{str(summary['release_lock_ready']).lower()}`", + f"- evidence consistency ok: `{str(summary['evidence_consistency_ok']).lower()}`", + "", + "This report is an operations cockpit for explicit-source SkillOps. It does not scan private logs, write source files, apply patches, or count as world-class external or human evidence.", + "", + "## Privacy Boundary", + "", + ] + contract = report["operations_contract"] + for key in [ + "explicit_source_required_for_scan", + "implicit_private_log_scan", + "raw_content_stored", + "redacted_excerpts_only", + "proposal_only", + "approval_required_for_writes", + "writes_source_files", + "auto_patch_enabled", + "daily_report_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(["", "## Opportunities", ""]) + opportunity_summary = report.get("opportunity_summary", {}) + lines.extend( + [ + f"- count: `{opportunity_summary.get('opportunity_count', 0)}`", + f"- top score: `{opportunity_summary.get('top_score', 0)}`", + f"- ready for approval review: `{opportunity_summary.get('ready_for_approval_review_count', 0)}`", + ] + ) + for opportunity in report.get("opportunities", []): + lines.extend( + [ + f"### {opportunity.get('title', '')}", + "", + f"- ID: `{opportunity.get('opportunity_id', '')}`", + f"- Action: `{opportunity.get('action_type', '')}`", + f"- Decision: `{opportunity.get('decision', '')}`", + f"- Score: `{opportunity.get('score', 0)}`", + f"- Risk: `{opportunity.get('risk_level', '')}`", + f"- Policy: {opportunity.get('policy_reason', '')}", + ] + ) + lines.extend(["", "## Patterns", ""]) + patterns = report.get("patterns", []) + if not patterns: + lines.append("- No repeated pattern met the support threshold.") + for pattern in patterns: + lines.append( + f"- `{pattern.get('pattern_id', '')}`: {pattern.get('label', '')} " + f"(support `{pattern.get('support_count', 0)}`, confidence `{pattern.get('confidence', '')}`)" + ) + lines.extend(["", "## Proposals", ""]) + proposals = report.get("proposals", []) + if not proposals: + lines.append("- No adaptation proposals are waiting for review.") + for proposal in proposals: + lines.extend( + [ + f"### {proposal['title']}", + "", + f"- ID: `{proposal['proposal_id']}`", + f"- Pattern: `{proposal['pattern_id']}`", + f"- Status: `{proposal['status']}`", + f"- Risk: `{proposal['risk_level']}`", + f"- Requires approval: `{str(proposal['requires_approval']).lower()}`", + "- Target files:", + ] + ) + lines.extend(f" - `{path}`" for path in proposal.get("target_files", [])) + lines.append("- Verification:") + lines.extend(f" - `{command}`" for command in proposal.get("verification_commands", [])) + lines.append("") + 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 Daily SkillOps report from explicit-source adaptive evidence.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--source", help="Explicit curated source file to scan before rendering the daily report.") + parser.add_argument("--patterns-json", default="reports/user_patterns.json") + parser.add_argument("--proposals-json", default="reports/adaptation_proposals.json") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--min-support", type=int, default=2) + parser.add_argument("--generated-at", default=utc_now()) + parser.add_argument("--allow-history-source", action="store_true") + parser.add_argument("--no-refresh-source-reports", action="store_true") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = build_report( + skill_dir, + args.generated_at, + source=Path(args.source) if args.source else None, + min_support=args.min_support, + allow_history_source=args.allow_history_source, + patterns_json=Path(args.patterns_json), + proposals_json=Path(args.proposals_json), + write_source_reports=not args.no_refresh_source_reports, + ) + output_json = resolve_output(skill_dir, args.output_json) if args.output_json else default_daily_path(skill_dir, args.generated_at, "json") + output_md = resolve_output(skill_dir, args.output_md) if args.output_md else default_daily_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() diff --git a/scripts/render_description_drift_history.py b/scripts/render_description_drift_history.py new file mode 100644 index 0000000..88578d7 --- /dev/null +++ b/scripts/render_description_drift_history.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +def load_snapshots(history_dir: Path) -> list[dict]: + snapshots = [] + for path in sorted(history_dir.glob("*.json")): + snapshots.append(json.loads(path.read_text(encoding="utf-8"))) + return snapshots + + +def render_markdown(snapshots: list[dict]) -> str: + lines = [ + "# Description Drift History", + "", + "| Date | Label | Target | Winner | Tokens | Holdout FP | Holdout FN | Blind FP | Blind FN | Judge Blind Errors | Judge Agreement | Adv FP | Adv FN | Adv Gap | Adv Risk | Drift Note |", + "| --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |", + ] + + previous_by_target: dict[str, dict] = {} + + def display(value: object) -> str: + return "-" if value is None else str(value) + + def family_display(target: dict, gate: str) -> str: + family = (target.get("family_health", {}) or {}).get(gate) or {} + if not family: + return "-" + weakest = family.get("weakest_family") or {} + weakest_label = weakest.get("family") or "-" + return f"{family.get('clean_family_count', 0)}/{family.get('family_count', 0)} clean; weakest={weakest_label}" + + for snapshot in snapshots: + for target in snapshot.get("targets", []): + previous = previous_by_target.get(target["name"]) + drift_note = target.get("drift_note") + if not drift_note and previous: + token_delta = target["winner_tokens"] - previous["winner_tokens"] + blind_prev = previous.get("winner_blind_holdout_total_errors") + blind_now = target.get("winner_blind_holdout_total_errors") + token_note = f"token delta {token_delta:+d}" + if blind_prev is None or blind_now is None: + blind_note = "blind gate unchanged" + else: + blind_note = f"blind error delta {blind_now - blind_prev:+d}" + adv_prev = previous.get("winner_adversarial_holdout_total_errors") + adv_now = target.get("winner_adversarial_holdout_total_errors") + if adv_prev is None or adv_now is None: + adv_note = "adversarial gate unchanged" + else: + adv_note = f"adversarial error delta {adv_now - adv_prev:+d}" + drift_note = f"{token_note}; {blind_note}; {adv_note}" + if not drift_note: + drift_note = "initial snapshot" + adversarial_calibration = (target.get("calibration", {}) or {}).get("adversarial_holdout") or {} + judge_blind = (target.get("judge_blind") or {}).get("winner") or {} + judge_errors = target.get("winner_judge_blind_holdout_total_errors") + lines.append( + f"| {snapshot['date']} | {snapshot['label']} | `{target['name']}` | `{target['winner_label']}` | {target['winner_tokens']} | {target['winner_holdout_fp']} | {target['winner_holdout_fn']} | {display(target.get('winner_blind_holdout_fp'))} | {display(target.get('winner_blind_holdout_fn'))} | {display(judge_errors)} | {display(judge_blind.get('agreement_rate'))} | {display(target.get('winner_adversarial_holdout_fp'))} | {display(target.get('winner_adversarial_holdout_fn'))} | {display(adversarial_calibration.get('score_gap'))} | {display(adversarial_calibration.get('risk_band'))} | {drift_note} |" + ) + previous_by_target[target["name"]] = target + + lines.extend( + [ + "", + "## Family Coverage", + "", + "| Date | Label | Target | Blind Families | Judge Blind Families | Adversarial Families |", + "| --- | --- | --- | --- | --- | --- |", + ] + ) + for snapshot in snapshots: + for target in snapshot.get("targets", []): + lines.append( + f"| {snapshot['date']} | {snapshot['label']} | `{target['name']}` | {family_display(target, 'blind_holdout')} | {family_display(target, 'judge_blind_holdout')} | {family_display(target, 'adversarial_holdout')} |" + ) + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render description optimization drift history.") + parser.add_argument("--history-dir", default="evals/history/description_optimization") + parser.add_argument("--output", default="reports/description_drift_history.md") + args = parser.parse_args() + + snapshots = load_snapshots(Path(args.history_dir)) + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(render_markdown(snapshots), encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/scripts/render_eval_dashboard.py b/scripts/render_eval_dashboard.py new file mode 100644 index 0000000..da7f58d --- /dev/null +++ b/scripts/render_eval_dashboard.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +import argparse +import json +import subprocess +import sys +from pathlib import Path + + +BEGIN_MARKER = "" +END_MARKER = "" + + +def run_eval_suite(args: argparse.Namespace) -> dict: + proc = subprocess.run( + [ + sys.executable, + "scripts/run_eval_suite.py", + "--eval-dir", + args.eval_dir, + "--description-file", + args.description_file, + "--baseline-description-file", + args.baseline_description_file, + ], + capture_output=True, + text=True, + check=True, + ) + return json.loads(proc.stdout) + + +def render_readme_panel(report: dict) -> str: + summary = report["summary"] + suites = report["suites"] + families = report["family_summary"] + + lines = [ + BEGIN_MARKER, + f"- regression corpus: `{summary['total_cases']}` prompts across `{summary['family_count']}` families", + f"- aggregate result: `{summary['false_positives']}` false positives, `{summary['false_negatives']}` false negatives, average precision `{summary['average_precision']}`, average recall `{summary['average_recall']}`", + "- suite status:", + "", + "| Suite | Cases | FP | FN | Precision | Recall |", + "| --- | ---: | ---: | ---: | ---: | ---: |", + ] + for suite_name in ("train", "dev", "holdout"): + suite = suites[suite_name] + cases = sum(bucket["total"] for bucket in suite["bucket_stats"].values()) + lines.append( + f"| {suite_name} | {cases} | {suite['false_positives']} | {suite['false_negatives']} | {suite['precision']} | {suite['recall']} |" + ) + + lines.extend( + [ + "", + "| Family | Cases | Pass Rate |", + "| --- | ---: | ---: |", + ] + ) + for family, stats in sorted(families.items()): + lines.append(f"| `{family}` | {stats['total']} | {stats['pass_rate']} |") + + lines.extend( + [ + "", + "Full reports: [reports/eval_suite.json](reports/eval_suite.json) and [reports/family_summary.md](reports/family_summary.md)", + END_MARKER, + ] + ) + return "\n".join(lines) + + +def render_family_summary(report: dict) -> str: + summary = report["summary"] + lines = [ + "# Eval Family Summary", + "", + f"- total cases: `{summary['total_cases']}`", + f"- families: `{summary['family_count']}`", + f"- false positives: `{summary['false_positives']}`", + f"- false negatives: `{summary['false_negatives']}`", + f"- average precision: `{summary['average_precision']}`", + f"- average recall: `{summary['average_recall']}`", + "", + "## Family Results", + "", + "| Family | Cases | Passed | FP | FN | Pass Rate |", + "| --- | ---: | ---: | ---: | ---: | ---: |", + ] + for family, stats in sorted(report["family_summary"].items()): + lines.append( + f"| `{family}` | {stats['total']} | {stats['passed']} | {stats['false_positives']} | {stats['false_negatives']} | {stats['pass_rate']} |" + ) + return "\n".join(lines) + "\n" + + +def update_readme(readme_path: Path, panel: str) -> None: + text = readme_path.read_text(encoding="utf-8") + if BEGIN_MARKER not in text or END_MARKER not in text: + raise SystemExit(f"README markers not found in {readme_path}") + start = text.index(BEGIN_MARKER) + end = text.index(END_MARKER) + len(END_MARKER) + updated = text[:start] + panel + text[end:] + readme_path.write_text(updated, encoding="utf-8") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render README-visible eval results and full family report.") + parser.add_argument("--eval-dir", default="evals") + parser.add_argument("--description-file", default="evals/improved_description.txt") + parser.add_argument("--baseline-description-file", default="evals/baseline_description.txt") + parser.add_argument("--output-dir", default="reports") + parser.add_argument("--readme", default="README.md") + args = parser.parse_args() + + report = run_eval_suite(args) + output_dir = Path(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + (output_dir / "eval_suite.json").write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + (output_dir / "family_summary.md").write_text(render_family_summary(report), encoding="utf-8") + update_readme(Path(args.readme), render_readme_panel(report)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_evidence_consistency.py b/scripts/render_evidence_consistency.py new file mode 100644 index 0000000..068c713 --- /dev/null +++ b/scripts/render_evidence_consistency.py @@ -0,0 +1,676 @@ +#!/usr/bin/env python3 +import argparse +import json +import subprocess +from datetime import date +from pathlib import Path +from typing import Any + +from evidence_consistency_artifact_roles import build_preflight_artifact_role_handoff_checks +from evidence_consistency_core import ( + ADOPTION_SUMMARY_KEYS, + BENCHMARK_SUMMARY_KEYS, + LEDGER_SUMMARY_KEYS, + LOCKSTEP_SECTIONS, + REQUIRED_REPORTS, + REQUIRED_TEXT_REPORTS, + add_check, + as_int, + beta_public_claim_split_values, + compare_summary_keys, + compare_values, + gate_by_key, + load_json, + load_text, + nested, + rel_path, + render_markdown, + report_contract, + scanned_surface_paths, +) +from evidence_consistency_phase_queue import build_phase_queue_consistency_check +from evidence_consistency_release import build_release_evidence_flow_check +from evidence_consistency_skill_os2_review import build_skill_os2_review_current_evidence_check +from evidence_consistency_world_class import build_world_class_workflow_check +from skill_ir_paths import find_skill_ir_path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders a cross-report evidence consistency gate for generated Skill OS reports." + + +def git_worktree_status(skill_dir: Path) -> dict[str, Any]: + try: + proc = subprocess.run( + ["git", "-C", str(skill_dir), "status", "--porcelain=v1", "-uall"], + capture_output=True, + text=True, + check=False, + ) + except OSError: + return {"available": False, "clean": None, "changed_file_count": None} + if proc.returncode != 0: + return {"available": False, "clean": None, "changed_file_count": None} + changes = [line for line in proc.stdout.splitlines() if line.strip()] + return {"available": True, "clean": not changes, "changed_file_count": len(changes)} + + +def build_review_studio_gate_action_mirror_check(review_studio: dict[str, Any]) -> dict[str, Any]: + gates = review_studio.get("gates", []) if isinstance(review_studio.get("gates", []), list) else [] + actions = ( + review_studio.get("review_actions", []) + if isinstance(review_studio.get("review_actions", []), list) + else [] + ) + non_pass_gates = [gate for gate in gates if isinstance(gate, dict) and gate.get("status") != "pass"] + pass_gates = [gate for gate in gates if isinstance(gate, dict) and gate.get("status") == "pass"] + actions_by_gate = { + str(action.get("gate_key", "")): action + for action in actions + if isinstance(action, dict) and str(action.get("gate_key", "")) + } + expected = { + "non_pass_gate_keys": sorted(str(gate.get("key", "")) for gate in non_pass_gates), + "action_gate_keys": sorted(str(gate.get("key", "")) for gate in non_pass_gates), + "pass_gate_action_ids_empty": True, + "gate_action_mirrors": { + str(gate.get("key", "")): { + "review_action_id": f"review-action-{gate.get('key', '')}", + "review_action_status": gate.get("status", ""), + "title_present": True, + "next_step_present": True, + "reason_present": True, + "source_ref_present": True, + "verification_command_present": True, + } + for gate in non_pass_gates + }, + } + actual = { + "non_pass_gate_keys": sorted(str(gate.get("key", "")) for gate in non_pass_gates), + "action_gate_keys": sorted(actions_by_gate), + "pass_gate_action_ids_empty": all(not gate.get("review_action_id") for gate in pass_gates), + "gate_action_mirrors": {}, + } + for gate in non_pass_gates: + key = str(gate.get("key", "")) + action = actions_by_gate.get(key, {}) + actual["gate_action_mirrors"][key] = { + "review_action_id": gate.get("review_action_id", ""), + "review_action_status": gate.get("review_action_status", ""), + "title_present": bool(gate.get("review_action_title")), + "next_step_present": bool(gate.get("review_action_next_step")), + "reason_present": bool(gate.get("review_action_reason")), + "source_ref_present": bool(gate.get("review_action_source_ref_count")), + "verification_command_present": bool(gate.get("review_action_verification_command")), + "top_level_action_id": action.get("action_id", ""), + "top_level_action_status": action.get("status", ""), + "top_level_title_present": bool(action.get("title")), + "top_level_next_step_present": bool(action.get("next_step")), + "top_level_reason_present": bool(action.get("reason")), + "top_level_source_ref_present": bool(action.get("source_refs")), + "top_level_verification_command_present": bool(action.get("verification_command")), + } + expected["gate_action_mirrors"][key]["top_level_action_id"] = f"review-action-{key}" + expected["gate_action_mirrors"][key]["top_level_action_status"] = gate.get("status", "") + expected["gate_action_mirrors"][key]["top_level_title_present"] = True + expected["gate_action_mirrors"][key]["top_level_next_step_present"] = True + expected["gate_action_mirrors"][key]["top_level_reason_present"] = True + expected["gate_action_mirrors"][key]["top_level_source_ref_present"] = True + expected["gate_action_mirrors"][key]["top_level_verification_command_present"] = True + return { + "key": "review-studio-gate-action-mirror", + "label": "Review Studio gates mirror review actions", + "status": "pass" if expected == actual else "fail", + "expected": expected, + "actual": actual, + "paths": [REQUIRED_REPORTS["review_studio"]], + "detail": ( + "Every non-pass Review Studio gate must have exactly one top-level review action and a gate-local " + "action summary with source refs and a verification command." + ), + } + + +def build_report(skill_dir: Path, generated_at: str) -> dict[str, Any]: + reports: dict[str, dict[str, Any]] = {} + text_reports: dict[str, str] = {} + checks: list[dict[str, Any]] = [] + load_failures: dict[str, str] = {} + for name, relative in REQUIRED_REPORTS.items(): + payload, failure = load_json(skill_dir / relative) + reports[name] = payload + if failure: + load_failures[relative] = failure + for name, relative in REQUIRED_TEXT_REPORTS.items(): + text, failure = load_text(skill_dir / relative) + text_reports[name] = text + if failure: + load_failures[relative] = failure + add_check( + checks, + key="required-report-artifacts", + label="Required report artifacts are readable", + status="pass" if not load_failures else "fail", + expected="all required JSON and Markdown reports exist and parse", + actual=load_failures or "all readable", + paths=list(REQUIRED_REPORTS.values()) + list(REQUIRED_TEXT_REPORTS.values()), + detail="The consistency gate can only be trusted when every source JSON report parses and every source Markdown report is readable.", + ) + checks.append(build_release_evidence_flow_check(skill_dir)) + + benchmark = reports["benchmark"] + overview = reports["overview"] + interpretation = reports["interpretation"] + adoption = reports["adoption"] + ledger = reports["world_class_ledger"] + world_class_plan = reports["world_class_plan"] + world_class_intake = reports["world_class_intake"] + world_class_preflight = reports["world_class_preflight"] + world_class_submission_review = reports["world_class_submission_review"] + world_class_operator_runbook = reports["world_class_operator_runbook"] + coverage = reports["skill_os2_coverage"] + review_studio = reports["review_studio"] + package_verification = reports["package_verification"] + install_simulation = reports["install_simulation"] + trust = reports["trust"] + context_budget = reports["context_budget"] + claim_guard = reports["world_class_claim_guard"] + + checks.append(build_review_studio_gate_action_mirror_check(review_studio)) + + benchmark_summary = nested(benchmark, ["summary"], {}) + adoption_summary = nested(adoption, ["summary"], {}) + ledger_summary = nested(ledger, ["summary"], {}) + coverage_summary = nested(coverage, ["summary"], {}) + studio_summary = nested(review_studio, ["summary"], {}) + package_summary = nested(package_verification, ["summary"], {}) + install_summary = nested(install_simulation, ["summary"], {}) + trust_summary = nested(trust, ["summary"], {}) + context_stats = nested(context_budget, ["stats"], {}) + claim_guard_summary = nested(claim_guard, ["summary"], {}) + preflight_summary = nested(world_class_preflight, ["summary"], {}) + context_governance = ( + context_stats.get("deferred_resource_governance", {}) if isinstance(context_stats, dict) else {} + ) + if not isinstance(context_governance, dict): + context_governance = {} + large_deferred_dirs = context_stats.get("large_deferred_resource_dirs", []) if isinstance(context_stats, dict) else [] + top_deferred = "none" + if isinstance(large_deferred_dirs, list) and large_deferred_dirs: + first = large_deferred_dirs[0] if isinstance(large_deferred_dirs[0], dict) else {} + top_deferred = f"{first.get('path', 'resource')} {first.get('estimated_tokens', 'n/a')}" + context_expected_status = "pass" if context_budget.get("ok") else "block" + if context_budget.get("warnings") and context_expected_status == "pass": + context_expected_status = "warn" + context_expected_detail = ( + f"initial load {context_stats.get('estimated_initial_load_tokens', 'n/a')}/" + f"{context_stats.get('context_budget_limit', 'n/a')}; " + f"deferred {context_stats.get('deferred_resource_tokens', 'n/a')}/" + f"{context_stats.get('deferred_resource_warn_threshold', 'n/a')}; " + f"top deferred {top_deferred}; " + f"resource governance {context_governance.get('status', 'unknown')}; " + f"quality density {context_stats.get('quality_density', 'n/a')}" + ) + context_gate = gate_by_key(review_studio, "context-budget") + compare_values( + checks, + key="review-studio-context-budget-mirror", + label="Review Studio mirrors context budget governance", + expected={ + "status": context_expected_status, + "detail": context_expected_detail, + "evidence": REQUIRED_REPORTS["context_budget"], + }, + actual={ + "status": context_gate.get("status"), + "detail": context_gate.get("detail"), + "evidence": context_gate.get("evidence"), + }, + paths=[REQUIRED_REPORTS["context_budget"], REQUIRED_REPORTS["review_studio"]], + detail=( + "Review Studio must not keep stale context warnings after context reports prove large deferred resources are governed." + ), + ) + if isinstance(benchmark_summary, dict): + compare_values( + checks, + key="benchmark-release-lock-self-consistency", + label="Benchmark release lock matches source dirty state", + expected=not bool(nested(benchmark, ["git_status", "source_dirty"], True)), + actual=benchmark_summary.get("release_lock_ready"), + paths=[REQUIRED_REPORTS["benchmark"]], + detail=( + "The benchmark release lock must be blocked by source changes, while generated evidence artifacts are " + "tracked as generation context." + ), + ) + current_git_status = git_worktree_status(skill_dir) + if current_git_status.get("available") and current_git_status.get("clean") is True: + compare_values( + checks, + key="benchmark-clean-worktree-release-lock", + label="Clean worktree keeps a clean benchmark release lock", + expected=True, + actual=benchmark_summary.get("release_lock_ready"), + paths=[REQUIRED_REPORTS["benchmark"]], + detail=( + "If the current worktree is clean, the committed benchmark report must not still carry a dirty release lock from an earlier generation." + ), + ) + else: + add_check( + checks, + key="benchmark-clean-worktree-release-lock", + label="Clean worktree keeps a clean benchmark release lock", + status="pass", + expected="checked only when git is available and the current worktree is clean", + actual=current_git_status, + paths=[REQUIRED_REPORTS["benchmark"]], + detail=( + "Dirty or non-git worktrees cannot prove final release-lock freshness, so this check is advisory until the final clean-lock pass." + ), + ) + skill_name = str(overview.get("name") or nested(review_studio, ["data", "frontmatter", "name"]) or skill_dir.name) + expected_skill_ir_path = find_skill_ir_path(skill_dir, skill_name, require_schema=True) + expected_skill_ir = { + "source_path": expected_skill_ir_path, + "exists": bool(expected_skill_ir_path and (skill_dir / expected_skill_ir_path).exists()), + "schema_version": load_json(skill_dir / expected_skill_ir_path)[0].get("schema_version") + if expected_skill_ir_path + else "", + } + actual_skill_ir = { + "overview_source_path": nested(overview, ["skill_ir", "source_path"], ""), + "interpretation_source_path": nested(interpretation, ["skill_ir", "source_path"], ""), + "review_studio_evidence_path": nested(review_studio, ["evidence_paths", "skill_ir"], ""), + "overview_deliverable": expected_skill_ir_path in nested(overview, ["skill_summary", "deliverables"], []), + "interpretation_deliverable": expected_skill_ir_path + in nested(interpretation, ["skill_summary", "deliverables"], []), + } + compare_values( + checks, + key="skill-ir-evidence-path-contract", + label="Human-facing reports expose the canonical Skill IR artifact", + expected={ + "overview_source_path": expected_skill_ir["source_path"], + "interpretation_source_path": expected_skill_ir["source_path"], + "review_studio_evidence_path": expected_skill_ir["source_path"], + "overview_deliverable": True, + "interpretation_deliverable": True, + "exists": True, + "schema_version": "2.0.0", + }, + actual={**actual_skill_ir, **{key: expected_skill_ir[key] for key in ("exists", "schema_version")}}, + paths=[ + REQUIRED_REPORTS["overview"], + REQUIRED_REPORTS["interpretation"], + REQUIRED_REPORTS["review_studio"], + expected_skill_ir_path or "skill-ir/examples", + ], + detail="Skill IR is the 2.0 platform-neutral semantic source, so user-facing reports must link to the artifact that actually exists.", + ) + for report_key, payload in [("overview", overview), ("interpretation", interpretation)]: + embedded_benchmark = nested(payload, ["benchmark_reproducibility"], {}) + compare_values( + checks, + key=f"{report_key}-benchmark-commit", + label=f"{report_key} embeds the benchmark commit", + expected=benchmark.get("commit"), + actual=nested(embedded_benchmark, ["commit"]), + paths=[REQUIRED_REPORTS["benchmark"], REQUIRED_REPORTS[report_key]], + detail="Human-facing reports must point to the same benchmark release-lock commit.", + ) + compare_summary_keys( + checks, + key_prefix=f"{report_key}-benchmark-summary", + label=f"{report_key} embeds benchmark summary fields", + source_summary=benchmark_summary if isinstance(benchmark_summary, dict) else {}, + embedded_summary=nested(embedded_benchmark, ["summary"], {}), + keys=BENCHMARK_SUMMARY_KEYS, + paths=[REQUIRED_REPORTS["benchmark"], REQUIRED_REPORTS[report_key]], + ) + compare_summary_keys( + checks, + key_prefix=f"{report_key}-adoption-summary", + label=f"{report_key} embeds adoption drift summary fields", + source_summary=adoption_summary if isinstance(adoption_summary, dict) else {}, + embedded_summary=nested(payload, ["adoption_drift", "summary"], {}), + keys=ADOPTION_SUMMARY_KEYS, + paths=[REQUIRED_REPORTS["adoption"], REQUIRED_REPORTS[report_key]], + ) + compare_summary_keys( + checks, + key_prefix=f"{report_key}-world-class-ledger-summary", + label=f"{report_key} embeds world-class ledger summary fields", + source_summary=ledger_summary if isinstance(ledger_summary, dict) else {}, + embedded_summary=nested(payload, ["world_class_evidence_ledger", "summary"], {}), + keys=LEDGER_SUMMARY_KEYS, + paths=[REQUIRED_REPORTS["world_class_ledger"], REQUIRED_REPORTS[report_key]], + ) + readiness_expected = { + "ready": ledger_summary.get("ready_to_claim_world_class") if isinstance(ledger_summary, dict) else None, + "decision": ledger_summary.get("decision") if isinstance(ledger_summary, dict) else None, + "pending_count": ledger_summary.get("pending_count") if isinstance(ledger_summary, dict) else None, + "accepted_count": ledger_summary.get("accepted_count") if isinstance(ledger_summary, dict) else None, + "source_check_count": ledger_summary.get("source_check_count") if isinstance(ledger_summary, dict) else None, + "source_pass_count": ledger_summary.get("source_pass_count") if isinstance(ledger_summary, dict) else None, + } + readiness = nested(payload, ["world_class_readiness"], {}) + readiness_actual = {key: readiness.get(key) if isinstance(readiness, dict) else None for key in readiness_expected} + compare_values( + checks, + key=f"{report_key}-world-class-readiness", + label=f"{report_key} derives readiness from the ledger", + expected=readiness_expected, + actual=readiness_actual, + paths=[REQUIRED_REPORTS["world_class_ledger"], REQUIRED_REPORTS[report_key]], + detail="Readiness summaries must be derived from the evidence ledger, not hand-maintained copy.", + ) + + for section in LOCKSTEP_SECTIONS: + compare_values( + checks, + key=f"overview-interpretation-lockstep-{section.replace('_', '-')}", + label=f"Overview and interpretation share {section}", + expected=overview.get(section), + actual=interpretation.get(section), + paths=[REQUIRED_REPORTS["overview"], REQUIRED_REPORTS["interpretation"]], + detail="The first-class interpretation report must stay in lockstep with the canonical overview model.", + ) + + for report_key, expected_html in [ + ("overview", "reports/skill-overview.html"), + ("interpretation", "reports/skill-interpretation.html"), + ]: + contract = report_contract(reports[report_key]) + expected = { + "schema_version": "2.0", + "default_language": "zh-CN", + "layout": "kami-white-audit-v2", + "html_report": expected_html, + "html_exists": True, + } + actual = { + "schema_version": contract.get("schema_version"), + "default_language": contract.get("default_language"), + "layout": contract.get("layout"), + "html_report": contract.get("html_report"), + "html_exists": (skill_dir / expected_html).exists(), + } + compare_values( + checks, + key=f"{report_key}-html-contract", + label=f"{report_key} has a stable HTML contract", + expected=expected, + actual=actual, + paths=[REQUIRED_REPORTS[report_key], expected_html], + detail="Report output paths and language defaults are part of the user-facing contract.", + ) + + if isinstance(ledger_summary, dict): + expected_boundary = { + "world_class_evidence_pending_count": ledger_summary.get("pending_count"), + "public_world_class_ready": ledger_summary.get("ready_to_claim_world_class"), + } + actual_boundary = { + "world_class_evidence_pending_count": coverage_summary.get("world_class_evidence_pending_count") + if isinstance(coverage_summary, dict) + else None, + "public_world_class_ready": coverage_summary.get("public_world_class_ready") + if isinstance(coverage_summary, dict) + else None, + } + compare_values( + checks, + key="coverage-world-class-boundary", + label="Coverage report mirrors world-class evidence boundary", + expected=expected_boundary, + actual=actual_boundary, + paths=[REQUIRED_REPORTS["world_class_ledger"], REQUIRED_REPORTS["skill_os2_coverage"]], + detail="Blueprint coverage can be locally complete while public world-class evidence remains pending.", + ) + benchmark_boundary = { + "world_class_ledger_pending_count": ledger_summary.get("pending_count"), + "world_class_source_check_count": ledger_summary.get("source_check_count"), + "world_class_source_pass_count": ledger_summary.get("source_pass_count"), + "world_class_source_blocked_count": ledger_summary.get("source_blocked_count"), + "public_claim_ready": ledger_summary.get("ready_to_claim_world_class"), + } + actual_benchmark_boundary = { + key: benchmark_summary.get(key) if isinstance(benchmark_summary, dict) else None + for key in benchmark_boundary + } + compare_values( + checks, + key="benchmark-world-class-boundary", + label="Benchmark report mirrors world-class evidence boundary", + expected=benchmark_boundary, + actual=actual_benchmark_boundary, + paths=[REQUIRED_REPORTS["world_class_ledger"], REQUIRED_REPORTS["benchmark"]], + detail="Benchmark reproducibility must not overstate public claim readiness.", + ) + beta_boundary, actual_beta_boundary = beta_public_claim_split_values( + benchmark, + ledger, + review_studio, + ) + compare_values( + checks, + key="benchmark-beta-public-claim-split", + label="Benchmark separates beta testing from public claims", + expected=beta_boundary, + actual=actual_beta_boundary, + paths=[REQUIRED_REPORTS["benchmark"], REQUIRED_REPORTS["world_class_ledger"]], + detail=( + "Beta/public testing may proceed with human blind review deferred, but public world-class or superiority claims must remain blocked." + ), + ) + preflight_boundary = { + "pending_count": ledger_summary.get("pending_count"), + "source_check_count": ledger_summary.get("source_check_count"), + "source_pass_count": ledger_summary.get("source_pass_count"), + "source_blocked_count": ledger_summary.get("source_blocked_count"), + "ready_to_claim_world_class": ledger_summary.get("ready_to_claim_world_class"), + "preflight_counts_as_evidence": False, + "credential_value_exposed": False, + } + actual_preflight_boundary = { + key: preflight_summary.get(key) if isinstance(preflight_summary, dict) else None + for key in preflight_boundary + } + compare_values( + checks, + key="preflight-world-class-boundary", + label="Preflight mirrors ledger without accepting evidence", + expected=preflight_boundary, + actual=actual_preflight_boundary, + paths=[REQUIRED_REPORTS["world_class_ledger"], REQUIRED_REPORTS["world_class_preflight"]], + detail="Collection preflight may help operators gather evidence, but it must not print secrets or change world-class readiness.", + ) + checks.extend( + build_preflight_artifact_role_handoff_checks( + skill_dir=skill_dir, + world_class_preflight=world_class_preflight, + review_studio=review_studio, + report_paths=REQUIRED_REPORTS, + ) + ) + checks.append( + build_phase_queue_consistency_check( + world_class_preflight=world_class_preflight, + world_class_operator_runbook=world_class_operator_runbook, + review_studio=review_studio, + report_paths=REQUIRED_REPORTS, + ) + ) + + public_ready = bool(ledger_summary.get("ready_to_claim_world_class")) if isinstance(ledger_summary, dict) else False + compare_values( + checks, + key="review-studio-no-overclaim", + label="Review Studio does not overclaim pending world-class evidence", + expected=False if not public_ready else True, + actual=studio_summary.get("decision") in {"pass", "release", "ready", "world-class-ready"} + if isinstance(studio_summary, dict) + else None, + paths=[REQUIRED_REPORTS["world_class_ledger"], REQUIRED_REPORTS["review_studio"]], + detail="When world-class evidence is pending, Review Studio must stay in a review or warning posture.", + ) + claim_surface_paths = scanned_surface_paths(claim_guard) + required_claim_surfaces = [ + "README.md", + "SKILL.md", + "manifest.json", + "agents/interface.yaml", + "evidence/world_class/README.md", + "security/permission_policy.json", + "reports/world_class_evidence_ledger.json", + ] + optional_claim_surfaces = [ + "dist/manifest.json", + "dist/targets/openai/adapter.json", + ] + required_claim_surfaces.extend( + path for path in optional_claim_surfaces if (skill_dir / path).exists() + ) + prohibited_claim_surface_prefixes = [ + "dist/install-simulation/", + "evidence/world_class/submissions/", + ] + json_claim_surface_count = as_int(claim_guard_summary.get("json_claim_surface_count")) + metadata_claim_surface_count = as_int(claim_guard_summary.get("metadata_claim_surface_count")) + package_claim_surface_count = as_int(claim_guard_summary.get("package_claim_surface_count")) + claim_surface_count = as_int(claim_guard_summary.get("claim_surface_count")) + expected_claim_guard_surface = { + "overclaim_guard_active": True, + "violation_count": 0, + "ledger_ready_to_claim_world_class": ledger_summary.get("ready_to_claim_world_class") + if isinstance(ledger_summary, dict) + else None, + "ledger_pending_count": ledger_summary.get("pending_count") if isinstance(ledger_summary, dict) else None, + "metadata_covers_json": True, + "package_surface_minimum": True, + "claim_surface_covers_package": True, + "required_surfaces": {path: True for path in required_claim_surfaces}, + "prohibited_surfaces": [], + } + actual_claim_guard_surface = { + "overclaim_guard_active": claim_guard_summary.get("overclaim_guard_active"), + "violation_count": claim_guard_summary.get("violation_count"), + "ledger_ready_to_claim_world_class": claim_guard_summary.get("ledger_ready_to_claim_world_class"), + "ledger_pending_count": claim_guard_summary.get("ledger_pending_count"), + "metadata_covers_json": ( + metadata_claim_surface_count is not None + and json_claim_surface_count is not None + and metadata_claim_surface_count >= json_claim_surface_count + ), + "package_surface_minimum": package_claim_surface_count is not None and package_claim_surface_count >= 5, + "claim_surface_covers_package": ( + claim_surface_count is not None + and package_claim_surface_count is not None + and claim_surface_count >= package_claim_surface_count + ), + "required_surfaces": {path: path in claim_surface_paths for path in required_claim_surfaces}, + "prohibited_surfaces": sorted( + path + for path in claim_surface_paths + if any(path.startswith(prefix) for prefix in prohibited_claim_surface_prefixes) + ), + } + compare_values( + checks, + key="claim-guard-package-runtime-surface", + label="Claim guard covers package and runtime claim surfaces", + expected=expected_claim_guard_surface, + actual=actual_claim_guard_surface, + paths=[ + REQUIRED_REPORTS["world_class_claim_guard"], + "manifest.json", + "agents/interface.yaml", + "dist/manifest.json", + "dist/targets/openai/adapter.json", + "evidence/world_class/README.md", + "security/permission_policy.json", + REQUIRED_REPORTS["world_class_ledger"], + ], + detail="The overclaim guard must scan package manifests, adapter metadata, security policy, and ledger surfaces before public readiness can be trusted.", + ) + checks.append( + build_world_class_workflow_check( + ledger=ledger, + world_class_plan=world_class_plan, + world_class_intake=world_class_intake, + world_class_submission_review=world_class_submission_review, + world_class_operator_runbook=world_class_operator_runbook, + review_studio=review_studio, + report_paths=REQUIRED_REPORTS, + ) + ) + skill_os2_review = text_reports.get("skill_os2_review", "") + checks.append( + build_skill_os2_review_current_evidence_check( + skill_dir=skill_dir, + skill_os2_review=skill_os2_review, + studio_summary=studio_summary if isinstance(studio_summary, dict) else {}, + trust_summary=trust_summary if isinstance(trust_summary, dict) else {}, + package_summary=package_summary if isinstance(package_summary, dict) else {}, + install_summary=install_summary if isinstance(install_summary, dict) else {}, + benchmark_summary=benchmark_summary if isinstance(benchmark_summary, dict) else {}, + context_stats=context_stats if isinstance(context_stats, dict) else {}, + required_text_reports=REQUIRED_TEXT_REPORTS, + required_reports=REQUIRED_REPORTS, + ) + ) + status_counts: dict[str, int] = {"pass": 0, "warn": 0, "fail": 0} + for check in checks: + status_counts[check["status"]] = status_counts.get(check["status"], 0) + 1 + summary = { + "check_count": len(checks), + "pass_count": status_counts.get("pass", 0), + "warn_count": status_counts.get("warn", 0), + "fail_count": status_counts.get("fail", 0), + "decision": "consistent" if status_counts.get("fail", 0) == 0 else "evidence-drift-detected", + } + return { + "schema_version": "1.0", + "ok": summary["fail_count"] == 0, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": summary, + "status_counts": status_counts, + "checks": checks, + "artifacts": { + "json": "reports/evidence_consistency.json", + "markdown": "reports/evidence_consistency.md", + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render cross-report evidence consistency checks.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json", default="reports/evidence_consistency.json") + parser.add_argument("--output-md", default="reports/evidence_consistency.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = build_report(skill_dir, args.generated_at) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_intent_confidence.py b/scripts/render_intent_confidence.py new file mode 100644 index 0000000..007b1cb --- /dev/null +++ b/scripts/render_intent_confidence.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +GENERIC_PHRASES = { + "turn a repeated workflow into a reusable skill", + "a reusable skill package", + "describe what the skill does and when to use it", + "turn rough requests into a compact reusable demo skill", +} + +GENERIC_TOKENS = { + "workflow", + "skill", + "package", + "reusable", + "repeated", + "request", + "requests", + "task", + "tasks", + "work", + "job", +} + +FOLLOW_UP_LIBRARY = { + "job_specificity": { + "slot": "job", + "question": "If you say it plainly, what concrete repeated task should this skill own every time?", + "why": "A skill needs a real recurring job, not only a generic packaging goal.", + "list": False, + }, + "real_inputs": { + "slot": "real_inputs", + "question": "What material will people actually hand to this skill in practice?", + "why": "Real input shape decides whether references, scripts, or examples are needed.", + "list": True, + }, + "primary_output": { + "slot": "primary_output", + "question": "What finished hand-back should this skill return so the next person can keep moving?", + "why": "The output is the anchor for package design and review.", + "list": False, + }, + "exclusions": { + "slot": "exclusions", + "question": "What nearby requests should this skill clearly leave out so the boundary stays clean?", + "why": "Exclusions are the fastest route to better trigger quality.", + "list": True, + }, + "constraints": { + "slot": "constraints", + "question": "What constraints matter most here: privacy, naming, compatibility, portability, governance, or speed?", + "why": "Constraints decide how much structure and validation this skill really needs.", + "list": True, + }, + "standards": { + "slot": "standards", + "question": "What quality bar matters most here: consistency, auditability, tone, or delivery speed?", + "why": "Standards explain how to choose the first evaluation gate.", + "list": True, + }, +} + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def normalized_list(value: list[str] | str | None) -> list[str]: + if value is None: + return [] + if isinstance(value, str): + parts = [item.strip() for item in value.split(",")] + return [item for item in parts if item] + return [str(item).strip() for item in value if str(item).strip()] + + +def tokenize(text: str) -> list[str]: + return re.findall(r"[A-Za-z][A-Za-z0-9_-]{2,}", text.lower()) + + +def is_generic(text: str) -> bool: + compact = " ".join(text.lower().split()) + if not compact: + return True + if compact in GENERIC_PHRASES: + return True + tokens = tokenize(compact) + if len(tokens) <= 3: + return True + content_tokens = [token for token in tokens if token not in GENERIC_TOKENS] + return len(content_tokens) < 2 + + +def build_context_from_skill(skill_dir: Path) -> dict[str, Any]: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _ = parse_frontmatter(skill_text) + payload = load_json(skill_dir / "reports" / "intent-context.json") + if payload: + return payload + return { + "job": frontmatter.get("description", ""), + "real_inputs": [], + "primary_output": "", + "description": frontmatter.get("description", ""), + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [], + } + + +def load_json(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + payload = json.loads(path.read_text(encoding="utf-8")) + return payload if isinstance(payload, dict) else {} + + +def assess_intent_confidence(context: dict[str, Any]) -> dict[str, Any]: + job = str(context.get("job", "")).strip() + primary_output = str(context.get("primary_output", "")).strip() + description = str(context.get("description", "")).strip() + real_inputs = normalized_list(context.get("real_inputs")) + exclusions = normalized_list(context.get("exclusions")) + constraints = normalized_list(context.get("constraints")) + standards = normalized_list(context.get("standards")) + user_references = normalized_list(context.get("user_references")) + correction = str(context.get("correction", "")).strip() + + score = 0 + strengths = [] + gaps = [] + + def add_gap(key: str, label: str, reason: str, severity: str = "high") -> None: + gaps.append({"key": key, "label": label, "reason": reason, "severity": severity}) + + if job and not is_generic(job): + score += 25 + strengths.append("The recurring job is concrete enough to anchor the package.") + elif job: + score += 10 + add_gap( + "job_specificity", + "Recurring job is still generic", + "The current job statement sounds more like a packaging goal than a concrete repeated task.", + ) + else: + add_gap("job_specificity", "Recurring job is missing", "The package has no clear job-to-be-done anchor yet.") + + if real_inputs: + score += 15 + strengths.append("Real input shape is explicit.") + else: + add_gap("real_inputs", "Real inputs are missing", "Without real inputs, it is hard to choose assets, scripts, or examples.") + + if primary_output and not is_generic(primary_output): + score += 20 + strengths.append("The hand-back output is concrete.") + elif primary_output: + score += 8 + add_gap( + "primary_output", + "Primary output is still generic", + "The current output does not yet say what a useful finished deliverable looks like.", + ) + else: + add_gap("primary_output", "Primary output is missing", "The package does not yet know what it must hand back.") + + if exclusions: + score += 15 + strengths.append("Boundary exclusions are already explicit.") + else: + add_gap("exclusions", "Near-neighbor exclusions are missing", "The route may blur into nearby requests without an exclusion list.") + + if constraints: + score += 10 + strengths.append("Operational constraints are visible.") + else: + add_gap("constraints", "Constraints are missing", "The package does not yet know which tradeoffs matter most.") + + if standards: + score += 5 + strengths.append("Quality standards are visible.") + else: + add_gap("standards", "Quality bar is implied, not explicit", "The first evaluation target is still underspecified.", "medium") + + if correction: + score += 5 + strengths.append("A correction loop already tightened the first reading.") + + if user_references: + score += 5 + strengths.append("Reference preferences are already available.") + + if description and not is_generic(description): + score += 5 + + score = min(score, 100) + if score >= 85: + band = "high" + elif score >= 70: + band = "medium" + else: + band = "low" + + gate_passed = score >= 70 and not any( + gap["key"] in {"job_specificity", "real_inputs", "primary_output"} and gap["severity"] == "high" + for gap in gaps + ) + follow_up_questions = [ + { + **FOLLOW_UP_LIBRARY[gap["key"]], + "label": gap["label"], + "severity": gap["severity"], + } + for gap in gaps + if gap["key"] in FOLLOW_UP_LIBRARY + ][:3] + + anchor_sentence = " ".join( + item + for item in [ + job or "Unclear recurring job.", + f"Primary output: {primary_output}." if primary_output else "", + f"Exclusions: {', '.join(exclusions)}." if exclusions else "", + ] + if item + ).strip() + + return { + "score": score, + "band": band, + "gate_passed": gate_passed, + "strengths": strengths[:5], + "gaps": gaps, + "follow_up_questions": follow_up_questions, + "anchor_sentence": anchor_sentence, + "recommended_action": ( + "Intent is clear enough to package the first routeable version." + if gate_passed + else "Pause before deep authoring and close the highest-leverage gaps first." + ), + "context": { + "job": job, + "real_inputs": real_inputs, + "primary_output": primary_output, + "description": description, + "exclusions": exclusions, + "constraints": constraints, + "standards": standards, + "correction": correction, + "user_references": user_references, + }, + } + + +def render_markdown(summary: dict[str, Any]) -> str: + lines = [ + "# Intent Confidence", + "", + f"- Confidence score: `{summary['score']}/100`", + f"- Confidence band: `{summary['band']}`", + f"- Gate passed: `{summary['gate_passed']}`", + f"- Recommended action: {summary['recommended_action']}", + "", + "## Current Reading", + "", + summary["anchor_sentence"] or "No clear anchor sentence yet.", + "", + "## Strong Signals", + "", + ] + if summary["strengths"]: + for item in summary["strengths"]: + lines.append(f"- {item}") + else: + lines.append("- No strong signals yet.") + + lines.extend(["", "## Gaps To Close", ""]) + if summary["gaps"]: + for gap in summary["gaps"]: + lines.append(f"- **{gap['label']}** (`{gap['severity']}`): {gap['reason']}") + else: + lines.append("- No major intent gaps detected.") + + lines.extend(["", "## Follow-Up Questions", ""]) + if summary["follow_up_questions"]: + for item in summary["follow_up_questions"]: + lines.append(f"- **{item['question']}**") + lines.append(f" - Why: {item['why']}") + else: + lines.append("- No extra follow-up questions required before the first package.") + return "\n".join(lines).strip() + "\n" + + +def render_intent_confidence( + skill_dir: Path, + context: dict[str, Any] | None = None, + output_md: Path | None = None, + output_json: Path | None = None, + context_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_md = output_md or reports_dir / "intent-confidence.md" + output_json = output_json or reports_dir / "intent-confidence.json" + context_json = context_json or reports_dir / "intent-context.json" + + context_payload = context or build_context_from_skill(skill_dir) + summary = assess_intent_confidence(context_payload) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + context_json.write_text(json.dumps(summary["context"], ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + "context_json": str(context_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render an intent confidence report for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--context-json") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + + context = None + if args.context_json: + context = load_json(Path(args.context_json).resolve()) + + result = render_intent_confidence( + Path(args.skill_dir), + context=context, + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_intent_dialogue.py b/scripts/render_intent_dialogue.py new file mode 100644 index 0000000..7d18e4e --- /dev/null +++ b/scripts/render_intent_dialogue.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def extract_title(body: str, fallback: str) -> str: + for line in body.splitlines(): + if line.startswith("# "): + return line[2:].strip() + return fallback + + +def classify_focus(description: str) -> str: + lowered = description.lower() + if any(token in lowered for token in ["review", "audit", "incident", "risk", "govern"]): + return "quality-and-boundary" + if any(token in lowered for token in ["export", "package", "adapter", "client", "portable"]): + return "portability-and-contract" + if any(token in lowered for token in ["workflow", "coordinate", "orchestrate", "process"]): + return "execution-and-assets" + return "trigger-and-output" + + +def build_questions(focus: str) -> list[dict]: + base = [ + { + "question": "If this skill worked beautifully, what recurring job would it reliably handle for the user every time?", + "why": "This reveals the real job-to-be-done and gives the package a humane center instead of a guessed prompt shape.", + }, + { + "question": "When someone reaches for this skill in the real world, what materials will they actually hand to it?", + "why": "Input shape decides whether references, scripts, or templates are needed.", + }, + { + "question": "What finished output should it hand back so the user can immediately keep moving?", + "why": "Outputs should drive the package structure before extra guidance is added.", + }, + { + "question": "Which nearby requests should this skill politely refuse so the boundary stays clean?", + "why": "The exclusion list is the fastest route to better trigger quality.", + }, + { + "question": "What matters most here: speed, consistency, auditability, portability, governance, or tone/style fit?", + "why": "Constraints decide how much structure, packaging, and review the skill actually needs.", + }, + { + "question": "Do you already have any references you want this skill to learn from, such as a repo, product, page, workflow, or prompt example?", + "why": "A good reference can raise the quality bar quickly, but the skill should only borrow patterns and standards, never copy wording or confidential material.", + }, + ] + if focus == "quality-and-boundary": + base.append( + { + "question": "What failure would make this skill untrustworthy in practice?", + "why": "The answer usually reveals the first evaluation gate worth adding.", + } + ) + elif focus == "portability-and-contract": + base.append( + { + "question": "Which environments or clients must be able to consume this skill?", + "why": "This sets the minimum metadata and degradation strategy.", + } + ) + else: + base.append( + { + "question": "What repeated manual step should become a deterministic asset first?", + "why": "This usually reveals whether a script or reference should be created next.", + } + ) + return base + + +def build_opening_styles() -> list[dict]: + return [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。", + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。", + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。", + }, + ] + + +def build_summary(skill_dir: Path) -> dict: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + name = frontmatter.get("name", skill_dir.name) + description = frontmatter.get("description", "No description found.") + title = extract_title(body, name.replace("-", " ").title()) + focus = classify_focus(description) + questions = build_questions(focus) + opening_styles = build_opening_styles() + output = { + "capability_sentence": f"{title} should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", + "required_capture": [ + "recurring job", + "real inputs", + "required outputs", + "exclusions", + "constraints", + "reference preferences", + "first evaluation target", + ], + "recommended_first_gate": "trigger and boundary" if focus != "portability-and-contract" else "portability and contract", + } + return { + "skill_name": name, + "title": title, + "description": description, + "focus": focus, + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", + "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster.", + ], + "opening_styles": opening_styles, + "optional_scaffold": [ + "The repeated job it should reliably handle", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse", + ], + "questions": questions, + "output": output, + } + + +def render_markdown(summary: dict) -> str: + lines = [ + "# Intent Dialogue", + "", + f"Skill: `{summary['skill_name']}`", + "", + "## Opening Frame", + "", + summary["opening_frame"], + "", + "## Opening Tone Options", + "", + ] + for item in summary["opening_styles"]: + lines.extend( + [ + f"### {item['label']}", + "", + f"- Best when: {item['best_when']}", + f"- Example: {item['message']}", + "", + ] + ) + lines.extend( + [ + "## Conversation Path", + "", + ] + ) + for idx, item in enumerate(summary["conversation_path"], start=1): + lines.append(f"{idx}. {item}") + lines.extend( + [ + "", + "## Why Start Here", + "", + "Use this short dialogue before deep authoring. The goal is to learn the real job, output, exclusions, and constraints so the first package is small but accurate.", + "", + "## Current Anchor", + "", + f"- Title: `{summary['title']}`", + f"- Description: {summary['description']}", + f"- Focus: `{summary['focus']}`", + f"- Reference note: {summary['reference_note']}", + "", + "## Questions To Ask", + "", + ] + ) + for idx, item in enumerate(summary["questions"], start=1): + lines.extend( + [ + f"{idx}. {item['question']}", + f" Why: {item['why']}", + ] + ) + lines.extend( + [ + "", + "## Capture Before Drafting", + "", + f"- Capability sentence: {summary['output']['capability_sentence']}", + f"- Recommended first gate: `{summary['output']['recommended_first_gate']}`", + "- Tiny optional scaffold:", + ] + ) + for item in summary["optional_scaffold"]: + lines.append(f" - {item}") + for item in summary["output"]["required_capture"]: + lines.append(f"- Capture: `{item}`") + return "\n".join(lines).strip() + "\n" + + +def render_intent_dialogue(skill_dir: Path, output_md: Path | None = None, output_json: Path | None = None) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "intent-dialogue.md" + output_json = output_json or reports_dir / "intent-dialogue.json" + + summary = build_summary(skill_dir) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8") + + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render an intent dialogue guide for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + result = render_intent_dialogue( + Path(args.skill_dir), + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_iteration_directions.py b/scripts/render_iteration_directions.py new file mode 100644 index 0000000..d4e86d1 --- /dev/null +++ b/scripts/render_iteration_directions.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def load_manifest(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def load_json(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def extract_sections(body: str) -> dict[str, str]: + sections: dict[str, list[str]] = {} + current = "_preamble" + sections[current] = [] + for line in body.splitlines(): + if line.startswith("## "): + current = line[3:].strip() + sections[current] = [] + continue + sections[current].append(line) + return {name: "\n".join(lines).strip() for name, lines in sections.items()} + + +def section_text(sections: dict[str, str], *names: str) -> str: + lowered = {key.lower(): value for key, value in sections.items()} + for name in names: + if name.lower() in lowered: + return lowered[name.lower()] + return "" + + +def has_any_files(path: Path) -> bool: + return path.exists() and any(path.iterdir()) + + +def direction(priority: int, title: str, why: str, actions: list[str], unlocks: str) -> dict: + return { + "priority": priority, + "title": title, + "why": why, + "actions": actions, + "unlocks": unlocks, + "do_now": "", + "wait_on": "", + } + + +def build_directions(skill_dir: Path) -> tuple[dict, list[dict]]: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + sections = extract_sections(body) + manifest = load_manifest(skill_dir / "manifest.json") + benchmark = load_json(skill_dir / "reports" / "github-benchmark-scan.json") + description = frontmatter.get("description", "") + maturity = manifest.get("maturity_tier", "scaffold") + + candidates: list[dict] = [] + if "Do not use" not in body and "exclusion" not in body.lower(): + candidates.append( + direction( + 1, + "Tighten trigger and exclusions", + "The package needs clearer near-neighbor exclusions before it grows.", + [ + "Add 3 to 5 should-trigger and should-not-trigger examples.", + "Refine the frontmatter description to name the recurring job and non-goals.", + "Run a first trigger evaluation pass before expanding the package.", + ], + "Cleaner routing and fewer accidental activations.", + ) + ) + if not has_any_files(skill_dir / "references") or not has_any_files(skill_dir / "scripts"): + candidates.append( + direction( + 2, + "Add the first execution asset", + "The package is still mostly prose. Add one asset that removes repeated manual work.", + [ + "Move stable procedural guidance into references if users will need it repeatedly.", + "Create one deterministic helper script if a repeated step can be executed instead of described.", + "Keep the main SKILL.md compact and route-oriented.", + ], + "Stronger execution quality without bloating the entrypoint.", + ) + ) + if benchmark.get("repositories"): + top_repo = benchmark["repositories"][0] + candidates.append( + direction( + 2, + "Borrow one proven pattern on purpose", + "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + [ + f"Read the strongest pattern from {top_repo.get('full_name', 'the top benchmark repo')}.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light.", + ], + "A cleaner package shape with less accidental over-design.", + ) + ) + if maturity == "scaffold": + candidates.append( + direction( + 3, + "Promote from scaffold to production-ready", + "The first version exists; the next gain usually comes from adding the smallest useful gates.", + [ + "Decide whether this skill is personal, team-reused, or library-grade.", + "Add only the gates that match that risk level.", + "Record lifecycle metadata and review cadence once reuse becomes real.", + ], + "A clearer path from exploratory package to maintained asset.", + ) + ) + workflow_section = section_text(sections, "Workflow", "Compact Workflow", "How To Use") + if workflow_section and len(workflow_section) < 120: + candidates.append( + direction( + 4, + "Deepen operator guidance", + "The workflow is still thin and may not be repeatable by another operator.", + [ + "Split the job into distinct phases or checkpoints.", + "Add success conditions and failure boundaries for each phase.", + "Link outputs to the step that produces them.", + ], + "Better repeatability and easier human review.", + ) + ) + if "portable" in description.lower() or "package" in description.lower(): + candidates.append( + direction( + 5, + "Harden portability semantics", + "The skill already signals reuse across environments, so contract clarity matters early.", + [ + "Confirm activation mode, execution context, and trust assumptions.", + "Add or review degradation strategy for non-native targets.", + "Package the skill once to verify adapter expectations.", + ], + "Safer cross-environment reuse with less target drift.", + ) + ) + + if len(candidates) < 3: + candidates.append( + direction( + len(candidates) + 10, + "Create an iteration evidence loop", + "The package should show what changed and why after the first draft.", + [ + "Generate the HTML skill report and keep it aligned with the package.", + "Record reference scan choices and non-goals.", + "Capture the next iteration choice explicitly before adding more files.", + ], + "A clearer path for the next author or reviewer.", + ) + ) + + chosen = sorted(candidates, key=lambda item: item["priority"])[:3] + for index, item in enumerate(chosen, start=1): + item["do_now"] = ( + "Do this first." + if index == 1 + else "Do this after the first move lands cleanly." + ) + item["wait_on"] = ( + "Wait to add broader structure until this move clearly improves reliability." + if index == 1 + else "Wait until the package has evidence that this extra structure is justified." + ) + summary = { + "skill_name": frontmatter.get("name", skill_dir.name), + "description": description, + "maturity_tier": maturity, + "selection_rule": "Pick the three smallest next steps that increase reliability more than they increase context cost.", + "recommended_now": chosen[0]["title"] if chosen else "", + "recommended_now_why": chosen[0]["why"] if chosen else "", + "defer_for_now": chosen[-1]["title"] if chosen else "", + } + return summary, chosen + + +def render_markdown(summary: dict, directions: list[dict]) -> str: + lines = [ + "# Iteration Directions", + "", + f"Skill: `{summary['skill_name']}`", + "", + f"- Maturity tier: `{summary['maturity_tier']}`", + f"- Selection rule: {summary['selection_rule']}", + f"- Start here: `{summary['recommended_now']}`", + f"- Why first: {summary['recommended_now_why']}", + f"- Defer for now: `{summary['defer_for_now']}`", + "", + "## Top 3 Next Moves", + "", + ] + for item in directions: + lines.extend( + [ + f"### {item['priority']}. {item['title']}", + "", + f"- Why now: {item['why']}", + f"- Timing: {item['do_now']}", + "- Recommended actions:", + ] + ) + for action in item["actions"]: + lines.append(f" - {action}") + lines.append(f"- Unlocks: {item['unlocks']}") + lines.append(f"- Wait on: {item['wait_on']}") + lines.append("") + return "\n".join(lines).strip() + "\n" + + +def render_iteration_directions(skill_dir: Path, output_md: Path | None = None, output_json: Path | None = None) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "iteration-directions.md" + output_json = output_json or reports_dir / "iteration-directions.json" + + summary, directions = build_directions(skill_dir) + payload = {"summary": summary, "directions": directions} + output_md.write_text(render_markdown(summary, directions), encoding="utf-8") + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + **payload, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render the top three next iteration directions for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + result = render_iteration_directions( + Path(args.skill_dir), + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_iteration_ledger.py b/scripts/render_iteration_ledger.py new file mode 100644 index 0000000..528577f --- /dev/null +++ b/scripts/render_iteration_ledger.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def load_snapshots(history_dir: Path) -> list[dict]: + return [load_json(path) for path in sorted(history_dir.glob("*.json"))] + + +def render_markdown( + system_snapshots: list[dict], description_snapshots: list[dict], route_scorecard: dict, promotion_decisions: dict +) -> str: + lines = [ + "# Iteration Ledger", + "", + "## System Milestones", + "", + "| Date | Label | Trigger Cases | FP | FN | Route Accuracy | Notes |", + "| --- | --- | ---: | ---: | ---: | ---: | --- |", + ] + for snapshot in system_snapshots: + trigger_summary = snapshot["trigger_summary"] + route_summary = snapshot.get("route_summary") or {} + route_accuracy = route_summary.get("accuracy") + route_display = "-" if route_accuracy is None else str(route_accuracy) + lines.append( + f"| {snapshot['date']} | {snapshot['label']} | {trigger_summary['total_cases']} | {trigger_summary['false_positives']} | {trigger_summary['false_negatives']} | {route_display} | {'; '.join(snapshot.get('notes', []))} |" + ) + + lines.extend( + [ + "", + "## Description Optimization Milestones", + "", + "| Date | Label | Target | Blind Errors | Judge Blind Errors | Judge Agreement | Adversarial Errors | Adversarial Gap | Adversarial Risk | Drift Note |", + "| --- | --- | --- | ---: | ---: | ---: | ---: | ---: | --- | --- |", + ] + ) + for snapshot in description_snapshots: + for target in snapshot.get("targets", []): + blind_errors = target.get("winner_blind_holdout_total_errors") + judge_blind_errors = target.get("winner_judge_blind_holdout_total_errors") + judge_blind = (target.get("judge_blind") or {}).get("winner") or {} + adversarial_errors = target.get("winner_adversarial_holdout_total_errors") + adversarial_calibration = (target.get("calibration", {}) or {}).get("adversarial_holdout") or {} + lines.append( + f"| {snapshot['date']} | {snapshot['label']} | `{target['name']}` | {'-' if blind_errors is None else blind_errors} | {'-' if judge_blind_errors is None else judge_blind_errors} | {judge_blind.get('agreement_rate', '-')} | {'-' if adversarial_errors is None else adversarial_errors} | {'-' if adversarial_calibration.get('score_gap') is None else adversarial_calibration.get('score_gap')} | {adversarial_calibration.get('risk_band', '-')} | {target.get('drift_note', '-')} |" + ) + + lines.extend( + [ + "", + "## Current Promotion Decisions", + "", + "| Target | Decision | Winner | Causes | Next Action |", + "| --- | --- | --- | --- | --- |", + ] + ) + for target in promotion_decisions.get("targets", []): + lines.append( + f"| `{target['name']}` | `{target['decision']}` | `{target['winner_label']}` | {', '.join(target['promotion']['causes'])} | {target['promotion']['next_action']} |" + ) + + lines.extend( + [ + "", + "## Current Route Scorecard", + "", + f"- total cases: `{route_scorecard['summary']['total_cases']}`", + f"- accuracy: `{route_scorecard['summary']['accuracy']}`", + f"- ambiguous cases: `{route_scorecard['summary']['ambiguous_case_count']}`", + "", + "| Route | Precision | Recall | Avg Margin |", + "| --- | ---: | ---: | ---: |", + ] + ) + for route_name, stats in route_scorecard["route_stats"].items(): + precision = "-" if stats["precision"] is None else stats["precision"] + recall = "-" if stats["recall"] is None else stats["recall"] + margin = "-" if stats["average_margin"] is None else stats["average_margin"] + lines.append(f"| `{route_name}` | {precision} | {recall} | {margin} |") + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render the iteration ledger from regression and routing data.") + parser.add_argument("--history-dir", default="evals/history") + parser.add_argument("--description-history-dir", default="evals/history/description_optimization") + parser.add_argument("--route-scorecard", default="reports/route_scorecard.json") + parser.add_argument("--promotion-decisions", default="reports/promotion_decisions.json") + parser.add_argument("--output", default="reports/iteration_ledger.md") + args = parser.parse_args() + + system_snapshots = load_snapshots(ROOT / args.history_dir) + description_snapshots = load_snapshots(ROOT / args.description_history_dir) + route_scorecard = load_json(ROOT / args.route_scorecard) + promotion_decisions = load_json(ROOT / args.promotion_decisions) + + output = ROOT / args.output + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text( + render_markdown(system_snapshots, description_snapshots, route_scorecard, promotion_decisions), + encoding="utf-8", + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_output_risk_profile.py b/scripts/render_output_risk_profile.py new file mode 100644 index 0000000..d060d15 --- /dev/null +++ b/scripts/render_output_risk_profile.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +RISK_FAMILIES = [ + { + "key": "tutorial_quality", + "label": "Tutorial quality", + "keywords": ["tutorial", "guide", "how to", "lesson", "course", "walkthrough", "教程", "指南", "课程"], + "risks": [ + "Generic section headings make the tutorial feel templated instead of fitted to the learner's task.", + "Steps may explain what to do without naming the exact check that proves the step worked.", + "Examples can become too abstract when the input material is concrete.", + ], + "constraints": [ + "Write headings from the user's domain nouns and desired outcome, not from generic labels like Overview or Key Points.", + "Pair each major step with a visible success check or expected intermediate output.", + "Use one concrete worked example before adding broad principles.", + ], + "self_repair": [ + "Replace generic H2/H3 headings with task-specific headings before final output.", + "Scan every numbered step for a missing verification cue.", + ], + }, + { + "key": "markdown_readability", + "label": "Markdown readability", + "keywords": ["markdown", "md", "table", "report", "brief", "doc", "文档", "表格", "报告", "排版"], + "risks": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Mixed heading levels can reduce scanability.", + ], + "constraints": [ + "Use tables only when comparison is the main job; otherwise prefer compact cards or grouped bullets.", + "Keep table cells short and move explanations below the table.", + "Use heading levels consistently and keep each section anchored to a user-facing outcome.", + ], + "self_repair": [ + "Preview whether each table still reads well when columns are narrow.", + "Convert any table with paragraph-length cells into bullets or cards.", + ], + }, + { + "key": "citation_clutter", + "label": "Citation and footnote clutter", + "keywords": ["citation", "source", "research", "footnote", "reference", "引用", "角标", "来源", "注释"], + "risks": [ + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Source notes may become more prominent than the tutorial itself.", + ], + "constraints": [ + "Attach citations only to claims that need evidence, not to every sentence.", + "Group source notes at the end of a section when inline markers would hurt readability.", + "Keep the main sentence readable without requiring the reader to chase a footnote.", + ], + "self_repair": [ + "Remove decorative citations that do not support a material claim.", + "Move repeated source explanations into one compact source note.", + ], + }, + { + "key": "visual_capture", + "label": "Screenshot and visual capture", + "keywords": ["screenshot", "image", "visual", "screen", "capture", "figma", "截图", "图片", "视觉", "截屏"], + "risks": [ + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Image descriptions can omit the action-relevant region.", + ], + "constraints": [ + "Never invent a screenshot; state when visual evidence is missing.", + "Record the source, viewport, and crop intent for any screenshot-dependent output.", + "Describe what the reader should inspect in the image, not just that an image exists.", + ], + "self_repair": [ + "Check that every screenshot reference points to a real provided or generated asset.", + "Reword any visual instruction that depends on an unseen screen state.", + ], + }, + { + "key": "code_or_command_safety", + "label": "Code and command safety", + "keywords": ["code", "script", "cli", "command", "terminal", "api", "代码", "脚本", "命令", "接口"], + "risks": [ + "Commands can omit environment assumptions, working directory, or rollback notes.", + "Code snippets can look runnable while missing required inputs.", + "Error handling can be either absent or over-engineered.", + ], + "constraints": [ + "Name the working directory, required inputs, and expected output for each command.", + "Mark destructive or external side-effect operations explicitly.", + "Prefer the smallest runnable snippet over broad framework code.", + ], + "self_repair": [ + "Scan each command for cwd, input, output, and side-effect assumptions.", + "Remove speculative error handling that is not tied to a real failure mode.", + ], + }, + { + "key": "tone_and_specificity", + "label": "Tone and specificity", + "keywords": ["marketing", "copy", "title", "article", "content", "summary", "标题", "文案", "文章", "总结"], + "risks": [ + "Headings and summaries can drift into generic, interchangeable language.", + "The output can sound polished but lose the user's actual taste, audience, or scenario.", + "Strong claims can appear without examples or constraints.", + ], + "constraints": [ + "Anchor titles and summaries in the user's audience, object, and concrete outcome.", + "Avoid placeholder phrases such as comprehensive guide, ultimate solution, or key insights unless the source demands them.", + "Preserve one distinctive phrase, constraint, or standard from the user's input.", + ], + "self_repair": [ + "Replace generic title candidates with scenario-specific alternatives.", + "Delete any polished sentence that could fit almost any project unchanged.", + ], + }, +] + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def load_json(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def normalized_text(skill_dir: Path) -> str: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + intent = load_json(skill_dir / "reports" / "intent-confidence.json") + context = intent.get("context", {}) if isinstance(intent, dict) else {} + parts = [ + skill_dir.name, + str(frontmatter.get("name", "")), + str(frontmatter.get("description", "")), + body, + str(context.get("job", "")), + str(context.get("primary_output", "")), + " ".join(context.get("real_inputs", []) or []), + " ".join(context.get("constraints", []) or []), + " ".join(context.get("standards", []) or []), + ] + return " ".join(parts).lower() + + +def match_family(text: str, family: dict) -> tuple[int, list[str]]: + hits = [] + for keyword in family["keywords"]: + if keyword.lower() in text: + hits.append(keyword) + return len(hits), hits + + +def build_summary(skill_dir: Path) -> dict: + text = normalized_text(skill_dir) + matched = [] + for family in RISK_FAMILIES: + score, hits = match_family(text, family) + if score: + matched.append({**family, "score": score, "matched_keywords": hits}) + if not matched: + matched = [ + { + **next(item for item in RISK_FAMILIES if item["key"] == "tone_and_specificity"), + "score": 0, + "matched_keywords": ["general-output-risk"], + }, + { + **next(item for item in RISK_FAMILIES if item["key"] == "markdown_readability"), + "score": 0, + "matched_keywords": ["general-output-risk"], + }, + ] + matched = sorted(matched, key=lambda item: item["score"], reverse=True)[:5] + top_risks = [] + constraints = [] + self_repair = [] + for item in matched: + top_risks.extend(item["risks"][:2]) + constraints.extend(item["constraints"][:2]) + self_repair.extend(item["self_repair"][:2]) + return { + "skill_name": skill_dir.name, + "risk_families": [ + { + "key": item["key"], + "label": item["label"], + "matched_keywords": item["matched_keywords"], + "score": item["score"], + "risks": item["risks"], + "constraints": item["constraints"], + "self_repair": item["self_repair"], + } + for item in matched + ], + "top_risks": dedupe(top_risks, 6), + "output_constraints": dedupe(constraints, 6), + "self_repair_checks": dedupe(self_repair, 6), + "reviewer_note": "Use this report before deepening the package and again before approving example outputs.", + } + + +def dedupe(items: list[str], limit: int) -> list[str]: + seen = set() + output = [] + for item in items: + if item in seen: + continue + seen.add(item) + output.append(item) + if len(output) == limit: + break + return output + + +def render_markdown(summary: dict) -> str: + lines = [ + "# Output Risk Profile", + "", + f"Skill: `{summary['skill_name']}`", + "", + "## Why This Exists", + "", + "Generated skills often fail in small output details: generic headings, cluttered citations, fragile screenshots, weak Markdown rendering, or missing execution assumptions. This profile predicts the most likely output mistakes before the skill is used heavily.", + "", + "## Matched Risk Families", + "", + ] + for family in summary["risk_families"]: + lines.extend( + [ + f"### {family['label']}", + f"- Matched keywords: {', '.join(family['matched_keywords'])}", + f"- Score: `{family['score']}`", + "", + ] + ) + lines.extend(["## Likely Output Mistakes", ""]) + for item in summary["top_risks"]: + lines.append(f"- {item}") + lines.extend(["", "## Output Constraints To Apply", ""]) + for item in summary["output_constraints"]: + lines.append(f"- {item}") + lines.extend(["", "## Self-Repair Checks", ""]) + for item in summary["self_repair_checks"]: + lines.append(f"- {item}") + lines.extend(["", "## Reviewer Note", "", summary["reviewer_note"], ""]) + return "\n".join(lines).strip() + "\n" + + +def render_output_risk_profile( + skill_dir: Path, + output_md: Path | None = None, + output_json: Path | None = None, +) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "output-risk-profile.md" + output_json = output_json or reports_dir / "output-risk-profile.json" + summary = build_summary(skill_dir) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render predicted output failure modes and quality constraints.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + result = render_output_risk_profile( + Path(args.skill_dir), + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_portability_report.py b/scripts/render_portability_report.py new file mode 100644 index 0000000..54b3db3 --- /dev/null +++ b/scripts/render_portability_report.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +import yaml + +from skill_ir_paths import find_skill_ir + + +ROOT = Path(__file__).resolve().parent.parent + + +def load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def load_yaml(path: Path) -> dict: + return yaml.safe_load(path.read_text(encoding="utf-8")) or {} + + +def find_ir(root: Path) -> tuple[dict, str]: + return find_skill_ir(root, root.name, fallback_source="missing") + + +def band_for(score: int) -> str: + if score >= 97: + return "world-class" + if score >= 93: + return "excellent" + if score >= 85: + return "strong" + return "developing" + + +def build_report(root: Path) -> dict: + interface_doc = load_yaml(root / "agents" / "interface.yaml") + compatibility = interface_doc.get("compatibility", {}) + activation = compatibility.get("activation", {}) + execution = compatibility.get("execution", {}) + trust = compatibility.get("trust", {}) + degradation = compatibility.get("degradation", {}) + targets = compatibility.get("adapter_targets", []) + expectations = load_json(root / "evals" / "packaging_expectations.json") + contracts_doc = (root / "references" / "packaging-contracts.md").exists() + matrix_doc = (root / "references" / "platform-capability-matrix.md").exists() + snapshot_files = list((root / "tests" / "snapshots").glob("*_adapter.json")) + ir, ir_source = find_ir(root) + has_neutral_metadata = (root / "SKILL.md").exists() and (root / "agents" / "interface.yaml").exists() + has_skill_ir = ir.get("schema_version") == "2.0.0" and bool(ir.get("job_to_be_done")) + + breakdown = { + "platform_neutral_semantic_source": 15 if has_neutral_metadata and has_skill_ir else 8 if has_neutral_metadata else 0, + "adapter_target_coverage": 15 if len(targets) >= 3 else 10 if len(targets) >= 2 else 5 if len(targets) >= 1 else 0, + "activation_metadata": 10 + if activation.get("mode") and activation.get("paths") is not None + else 5 + if activation.get("mode") + else 0, + "execution_metadata": 10 + if execution.get("context") and execution.get("shell") + else 0, + "trust_boundary_metadata": 15 + if trust.get("source_tier") and trust.get("remote_inline_execution") and trust.get("remote_metadata_policy") + else 0, + "degradation_strategy_coverage": 10 if all(degradation.get(target) for target in targets) else 0, + "contract_and_expectation_coverage": 15 + if contracts_doc and expectations.get("required_targets") == targets + else 5 + if contracts_doc + else 0, + } + + snapshot_score = 0 + if matrix_doc: + snapshot_score += 5 + if len(snapshot_files) >= len(targets): + snapshot_score += 5 + breakdown["consumer_validation_and_snapshots"] = snapshot_score + + score = sum(breakdown.values()) + return { + "ok": score >= 95, + "score": score, + "band": band_for(score), + "summary": { + "adapter_target_count": len(targets), + "activation_mode": activation.get("mode"), + "execution_context": execution.get("context"), + "shell": execution.get("shell"), + "trust_level": trust.get("source_tier"), + "remote_inline_execution": trust.get("remote_inline_execution"), + "degradation_coverage": sum(1 for target in targets if degradation.get(target)), + "snapshot_count": len(snapshot_files), + "ir_source": ir_source, + "ir_schema_version": ir.get("schema_version", "missing"), + }, + "breakdown": breakdown, + } + + +def render_markdown(report: dict) -> str: + lines = [ + "# Portability Score", + "", + f"- score: `{report['score']}/100`", + f"- band: `{report['band']}`", + f"- adapter targets: `{report['summary']['adapter_target_count']}`", + f"- activation mode: `{report['summary']['activation_mode']}`", + f"- execution context: `{report['summary']['execution_context']}`", + f"- shell: `{report['summary']['shell']}`", + f"- trust level: `{report['summary']['trust_level']}`", + f"- remote inline execution: `{report['summary']['remote_inline_execution']}`", + f"- Skill IR source: `{report['summary']['ir_source']}`", + f"- Skill IR schema: `{report['summary']['ir_schema_version']}`", + "", + "| Component | Score |", + "| --- | ---: |", + ] + for name, value in report["breakdown"].items(): + lines.append(f"| `{name}` | {value} |") + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a portability score from neutral metadata, contracts, and snapshots.") + parser.add_argument("skill_dir", nargs="?", default=str(ROOT), help="Skill directory to inspect. Defaults to this repo root.") + parser.add_argument("--output-json", default="reports/portability_score.json") + parser.add_argument("--output-md", default="reports/portability_score.md") + args = parser.parse_args() + + skill_root = Path(args.skill_dir).resolve() + report = build_report(skill_root) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_root / output_json + if not output_md.is_absolute(): + output_md = skill_root / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_prompt_quality_profile.py b/scripts/render_prompt_quality_profile.py new file mode 100644 index 0000000..afb0a07 --- /dev/null +++ b/scripts/render_prompt_quality_profile.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +TASK_FAMILIES = [ + { + "key": "creative_generation", + "label": "Creative generation", + "keywords": ["creative", "idea", "copy", "campaign", "title", "content", "concept", "创意", "文案", "标题", "内容"], + "role_guidance": "Use a taste-aware creator role with clear audience, tone, and originality boundaries.", + "task_guidance": "Generate variants, explain selection logic, and preserve the user's distinctive constraints.", + "format_guidance": "Return options with rationale, selection criteria, and refinement paths.", + }, + { + "key": "analytical_reasoning", + "label": "Analytical reasoning", + "keywords": ["analysis", "analyze", "diagnose", "compare", "synthesis", "decision", "评估", "分析", "诊断", "对比", "决策"], + "role_guidance": "Use an analyst role that separates evidence, inference, uncertainty, and recommendation.", + "task_guidance": "State assumptions, compare alternatives, and make the decision path inspectable.", + "format_guidance": "Return findings, evidence, tradeoffs, recommendation, and residual risks.", + }, + { + "key": "execution_operation", + "label": "Execution operation", + "keywords": ["workflow", "runbook", "execute", "operate", "checklist", "standardize", "流程", "操作", "执行", "清单", "标准化"], + "role_guidance": "Use an operator role with explicit boundaries, inputs, outputs, and failure handling.", + "task_guidance": "Convert the job into ordered steps with validation checks and stop conditions.", + "format_guidance": "Return a runbook-like handoff with commands, checks, owners, and next actions when relevant.", + }, + { + "key": "teaching_guidance", + "label": "Teaching guidance", + "keywords": ["tutorial", "teach", "lesson", "guide", "course", "coach", "教程", "教学", "课程", "指导", "老师"], + "role_guidance": "Use a teacher role that adapts to learner level and avoids overloading the first pass.", + "task_guidance": "Explain through progressive steps, examples, and visible success checks.", + "format_guidance": "Return learner-facing sections, worked examples, checkpoints, and common mistakes.", + }, + { + "key": "dialogue_interaction", + "label": "Dialogue interaction", + "keywords": ["dialogue", "interview", "conversation", "support", "chat", "discovery", "对话", "访谈", "客服", "沟通"], + "role_guidance": "Use a conversational role that asks only high-leverage questions and remembers the user's goal.", + "task_guidance": "Clarify intent, resolve uncertainty, and converge toward a recommendation instead of a long option list.", + "format_guidance": "Return concise prompts, decision points, and reviewer-visible assumptions.", + }, + { + "key": "prompt_engineering", + "label": "Prompt engineering", + "keywords": ["prompt", "metaprompt", "meta prompt", "instruction", "role", "format", "rtf", "提示词", "元提示词", "指令"], + "role_guidance": "Use a prompt engineer role only when role design materially improves execution.", + "task_guidance": "Map Role, Task, and Format into skill behavior rather than copying a large prompt template.", + "format_guidance": "Return a compact prompt contract plus tests, quality matrix, and usage notes.", + }, +] + + +QUALITY_DIMENSIONS = [ + { + "key": "completeness", + "label": "Completeness", + "signals": ["input", "output", "constraint", "standard", "example", "输入", "输出", "约束", "标准"], + "repair": "Name missing inputs, outputs, constraints, or success standards before deepening the package.", + }, + { + "key": "clarity", + "label": "Clarity", + "signals": ["clear", "specific", "unambiguous", "明确", "清晰", "具体"], + "repair": "Replace broad verbs with observable actions and define what done means.", + }, + { + "key": "consistency", + "label": "Consistency", + "signals": ["consistent", "aligned", "boundary", "一致", "边界", "对齐"], + "repair": "Check that role, task, format, exclusions, and examples do not contradict each other.", + }, + { + "key": "practicality", + "label": "Practicality", + "signals": ["action", "execute", "use", "workflow", "落地", "执行", "使用"], + "repair": "Add runnable steps, examples, or verification cues instead of abstract advice.", + }, + { + "key": "specificity", + "label": "Specificity", + "signals": ["audience", "domain", "scenario", "tone", "用户", "场景", "领域", "风格"], + "repair": "Anchor wording in the user's audience, domain nouns, and target outcome.", + }, +] + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def load_json(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def normalized_context(skill_dir: Path) -> tuple[str, dict]: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + intent = load_json(skill_dir / "reports" / "intent-confidence.json") + context = intent.get("context", {}) if isinstance(intent, dict) else {} + parts = [ + skill_dir.name, + str(frontmatter.get("name", "")), + str(frontmatter.get("description", "")), + body, + str(context.get("job", "")), + str(context.get("primary_output", "")), + " ".join(context.get("real_inputs", []) or []), + " ".join(context.get("constraints", []) or []), + " ".join(context.get("standards", []) or []), + str(context.get("correction", "")), + ] + return " ".join(parts).lower(), context + + +def keyword_hits(text: str, keywords: list[str]) -> list[str]: + return [keyword for keyword in keywords if keyword.lower() in text] + + +def complexity_band(text: str, context: dict, matched_count: int) -> dict: + list_signal_count = sum( + len(context.get(key, []) or []) + for key in ("real_inputs", "constraints", "standards", "exclusions", "user_references") + if isinstance(context.get(key, []), list) + ) + expert_terms = ["governance", "eval", "audit", "security", "compliance", "expert", "治理", "评测", "审计", "合规"] + score = matched_count + list_signal_count + score += 2 if any(term in text for term in expert_terms) else 0 + if score >= 8: + return {"band": "expert", "score": score, "reason": "multiple task families plus governance, evaluation, or expert-level constraints"} + if score >= 5: + return {"band": "complex", "score": score, "reason": "multiple inputs, constraints, or task families require tradeoff handling"} + if score >= 3: + return {"band": "medium", "score": score, "reason": "some judgment and multi-step structure are needed"} + return {"band": "simple", "score": score, "reason": "single dominant task shape with limited constraints"} + + +def need_model(context: dict, description: str) -> dict: + return { + "explicit_need": context.get("job") or description, + "implicit_need": "The reusable skill needs a stable role, task, and output contract rather than a one-off prompt.", + "scenario": ", ".join(context.get("real_inputs", []) or []) or "not yet explicit", + "user_level": "infer from examples and standards; ask only if it changes output depth", + "success_standard": ", ".join(context.get("standards", []) or []) or context.get("primary_output") or "usable output with clear validation cues", + } + + +def quality_matrix(text: str, context: dict) -> list[dict]: + matrix = [] + for dimension in QUALITY_DIMENSIONS: + hits = keyword_hits(text, dimension["signals"]) + score = 80 + if hits: + score += min(15, len(hits) * 5) + if dimension["key"] == "completeness": + has_output = bool(context.get("primary_output")) + has_inputs = bool(context.get("real_inputs")) + score += 5 if has_output and has_inputs else -15 + if dimension["key"] == "specificity" and not (context.get("standards") or context.get("constraints")): + score -= 10 + score = max(0, min(100, score)) + matrix.append( + { + "key": dimension["key"], + "label": dimension["label"], + "score": score, + "matched_signals": hits, + "repair": dimension["repair"], + } + ) + return matrix + + +def build_summary(skill_dir: Path) -> dict: + text, context = normalized_context(skill_dir) + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _ = parse_frontmatter(skill_text) + description = str(frontmatter.get("description", "")) + matched = [] + for family in TASK_FAMILIES: + hits = keyword_hits(text, family["keywords"]) + if hits: + matched.append({**family, "score": len(hits), "matched_keywords": hits}) + if not matched: + fallback = next(item for item in TASK_FAMILIES if item["key"] == "execution_operation") + matched = [{**fallback, "score": 0, "matched_keywords": ["general-skill"]}] + matched = sorted(matched, key=lambda item: item["score"], reverse=True)[:4] + primary = matched[0] + complexity = complexity_band(text, context, len(matched)) + matrix = quality_matrix(text, context) + overall = round(sum(item["score"] for item in matrix) / len(matrix), 1) + rtf_mapping = { + "role": primary["role_guidance"], + "task": primary["task_guidance"], + "format": primary["format_guidance"], + } + return { + "skill_name": skill_dir.name, + "relevance": "prompt-heavy" if any(item["key"] == "prompt_engineering" for item in matched) else "prompt-aware", + "primary_task_family": { + "key": primary["key"], + "label": primary["label"], + "matched_keywords": primary["matched_keywords"], + }, + "task_families": [ + { + "key": item["key"], + "label": item["label"], + "score": item["score"], + "matched_keywords": item["matched_keywords"], + "role_guidance": item["role_guidance"], + "task_guidance": item["task_guidance"], + "format_guidance": item["format_guidance"], + } + for item in matched + ], + "complexity": complexity, + "need_model": need_model(context, description), + "rtf_to_skill": rtf_mapping, + "quality_matrix": matrix, + "overall_quality_score": overall, + "self_repair_checks": [ + "Check explicit need, implicit need, scenario, user level, and success standard before deepening.", + "Map Role, Task, and Format into skill behavior, not decorative prompt labels.", + "Ask one focused clarification only when missing information changes the package boundary.", + "Add tests or examples for prompt-heavy behavior before treating it as reusable.", + "Keep prompt methodology in references and reports instead of bloating SKILL.md.", + ], + "reviewer_note": "Use this profile when the package depends on prompt behavior, role design, output contracts, or conversation quality.", + } + + +def render_markdown(summary: dict) -> str: + lines = [ + "# Prompt Quality Profile", + "", + f"Skill: `{summary['skill_name']}`", + f"Relevance: `{summary['relevance']}`", + f"Overall quality score: `{summary['overall_quality_score']}/100`", + "", + "## Primary Task Family", + "", + f"**{summary['primary_task_family']['label']}**", + f"- Matched keywords: {', '.join(summary['primary_task_family']['matched_keywords'])}", + "", + "## Complexity", + "", + f"- Band: `{summary['complexity']['band']}`", + f"- Score: `{summary['complexity']['score']}`", + f"- Reason: {summary['complexity']['reason']}", + "", + "## Need Model", + "", + ] + for key, value in summary["need_model"].items(): + lines.append(f"- {key.replace('_', ' ').title()}: {value}") + lines.extend(["", "## RTF To Skill Mapping", ""]) + for key, value in summary["rtf_to_skill"].items(): + lines.append(f"- {key.title()}: {value}") + lines.extend(["", "## Quality Matrix", ""]) + for item in summary["quality_matrix"]: + signals = ", ".join(item["matched_signals"]) if item["matched_signals"] else "none" + lines.extend( + [ + f"### {item['label']} — {item['score']}/100", + f"- Matched signals: {signals}", + f"- Repair: {item['repair']}", + "", + ] + ) + lines.extend(["## Matched Task Families", ""]) + for family in summary["task_families"]: + lines.extend( + [ + f"### {family['label']}", + f"- Score: `{family['score']}`", + f"- Keywords: {', '.join(family['matched_keywords'])}", + f"- Role: {family['role_guidance']}", + f"- Task: {family['task_guidance']}", + f"- Format: {family['format_guidance']}", + "", + ] + ) + lines.extend(["## Self-Repair Checks", ""]) + for item in summary["self_repair_checks"]: + lines.append(f"- {item}") + lines.extend(["", "## Reviewer Note", "", summary["reviewer_note"], ""]) + return "\n".join(lines).strip() + "\n" + + +def render_prompt_quality_profile( + skill_dir: Path, + output_md: Path | None = None, + output_json: Path | None = None, +) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "prompt-quality-profile.md" + output_json = output_json or reports_dir / "prompt-quality-profile.json" + summary = build_summary(skill_dir) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render prompt behavior quality, RTF mapping, and need-model checks for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + result = render_prompt_quality_profile( + Path(args.skill_dir), + Path(args.output_md).resolve() if args.output_md else None, + Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_reference_scan.py b/scripts/render_reference_scan.py new file mode 100644 index 0000000..a0dcdf8 --- /dev/null +++ b/scripts/render_reference_scan.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def extract_title(body: str, fallback: str) -> str: + for line in body.splitlines(): + if line.startswith("# "): + return line[2:].strip() + return fallback + + +def parse_reference(value: str, source: str) -> dict: + if "::" in value: + parts = [part.strip() for part in value.split("::")] + while len(parts) < 4: + parts.append("") + name, category, borrow, avoid = parts[:4] + else: + name, category, borrow, avoid = value.strip(), "", "", "" + defaults = { + "external": ( + "general", + "Extract the strongest reusable method, structure, or execution pattern.", + "Do not copy brand language, heavyweight process, or source-specific constraints.", + ), + "user": ( + "taste", + "Learn what quality, tone, workflow shape, or operating standard the user wants to preserve.", + "Do not copy wording, confidential material, or source-specific implementation details.", + ), + "local": ( + "general", + "Keep the new skill compatible with real local constraints that matter.", + "Do not inherit private or outdated patterns just because they already exist locally.", + ), + } + default_category, default_borrow, default_avoid = defaults.get(source, defaults["external"]) + return { + "name": name or "Unnamed reference", + "category": category or default_category, + "borrow": borrow or default_borrow, + "avoid": avoid or default_avoid, + "source": source, + } + + +def infer_scan_focus(skill_dir: Path, description: str) -> list[dict]: + checks = [] + if (skill_dir / "evals").exists(): + checks.append( + { + "label": "Evaluation pattern", + "reason": "This skill already carries eval assets, so benchmark how top examples define trigger boundaries and quality gates.", + } + ) + if (skill_dir / "scripts").exists(): + checks.append( + { + "label": "Execution pattern", + "reason": "There is deterministic logic in scripts, so compare how strong references separate prose from executable steps.", + } + ) + if (skill_dir / "agents" / "interface.yaml").exists(): + checks.append( + { + "label": "Portability pattern", + "reason": "The package carries neutral metadata, so scan how good references preserve semantics across targets without forking source.", + } + ) + checks.append( + { + "label": "Method pattern", + "reason": f"Use the core job description as the anchor for comparison: {description}", + } + ) + return checks[:4] + + +def split_references(references: list[dict]) -> tuple[list[dict], list[dict], list[dict]]: + externals = [item for item in references if item.get("source") == "external"] + users = [item for item in references if item.get("source") == "user"] + locals_ = [item for item in references if item.get("source") == "local"] + return externals, users, locals_ + + +def build_summary(skill_dir: Path, references: list[dict]) -> dict: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + name = frontmatter.get("name", skill_dir.name) + description = frontmatter.get("description", "No description found.") + title = extract_title(body, name.replace("-", " ").title()) + focus = infer_scan_focus(skill_dir, description) + external_references, user_references, local_constraints = split_references(references) + + borrow_plan = [ + "External benchmark first: let high-quality public references define the upper bound for method, structure, execution, or portability.", + "User references second: use them to understand taste, standards, and directional preferences without copying source phrasing.", + "Local fit third: use local assets only to detect naming conflicts, private dependencies, or compatibility constraints.", + "Borrow patterns, not prose: extract loops, boundaries, metadata, and operator flow without copying source-specific language.", + "Keep the package light: reject any borrowed pattern that increases context cost faster than it increases reliability.", + ] + + if external_references: + borrow_plan.append("Apply only the benchmark patterns that fit the chosen archetype, gates, and context budget.") + + return { + "skill_name": name, + "title": title, + "description": description, + "scan_focus": focus, + "external_references": external_references, + "user_references": user_references, + "local_constraints": local_constraints, + "borrow_plan": borrow_plan, + "priority_rule": "External benchmark objects set the pattern ceiling. User references refine taste and standards. Local files only calibrate fit, risk, and compatibility.", + "non_goals": [ + "Do not copy source prose or branding into the new skill.", + "Do not import gates that cost more context than they save.", + "Do not use benchmark scanning to justify scope creep.", + "Do not let local historical habits outrank stronger public benchmarks.", + ], + } + + +def render_markdown(summary: dict) -> str: + lines = [ + "# Reference Scan", + "", + f"Skill: `{summary['skill_name']}`", + "", + "## Why This Step Exists", + "", + "Use a short benchmark pass before authoring the package in depth. External benchmark objects should define the pattern ceiling. Local files are used afterward only to calibrate fit, privacy, naming, and compatibility.", + "", + "## Current Skill Anchor", + "", + f"- Title: `{summary['title']}`", + f"- Description: {summary['description']}", + "", + "## Scan Focus", + "", + ] + for item in summary["scan_focus"]: + lines.append(f"- **{item['label']}**: {item['reason']}") + + lines.extend(["", "## Priority Rule", "", f"- {summary['priority_rule']}", ""]) + + lines.extend(["## External Benchmark Objects", ""]) + if summary["external_references"]: + for ref in summary["external_references"]: + lines.extend( + [ + f"### {ref['name']}", + f"- Category: `{ref['category']}`", + f"- Borrow: {ref['borrow']}", + f"- Avoid: {ref['avoid']}", + "", + ] + ) + else: + lines.extend( + [ + "- No explicit external benchmark objects recorded yet.", + "- Recommended: capture 2 to 5 external references at most.", + "- Suggested mix: one method reference, one structure reference, one execution or portability reference.", + "", + ] + ) + + lines.extend(["## User-Supplied References", ""]) + if summary["user_references"]: + for ref in summary["user_references"]: + lines.extend( + [ + f"### {ref['name']}", + f"- Category: `{ref['category']}`", + f"- Learn from: {ref['borrow']}", + f"- Do not copy: {ref['avoid']}", + "", + ] + ) + else: + lines.extend( + [ + "- No user-supplied references recorded yet.", + "- Ask whether the user has a repo, product, page, workflow, or prompt example worth learning from.", + "- Treat these as pattern references only, not as text to be copied.", + "", + ] + ) + + lines.extend(["## Local Fit Check", ""]) + if summary["local_constraints"]: + for ref in summary["local_constraints"]: + lines.extend( + [ + f"### {ref['name']}", + f"- Category: `{ref['category']}`", + f"- Keep in mind: {ref['borrow']}", + f"- Do not inherit: {ref['avoid']}", + "", + ] + ) + else: + lines.extend( + [ + "- No local constraints recorded yet.", + "- Use this section for naming collisions, private dependencies, compatibility limits, or existing library conventions.", + "", + ] + ) + + lines.extend(["## Borrow Plan", ""]) + for item in summary["borrow_plan"]: + lines.append(f"- {item}") + + lines.extend(["", "## Non-Goals", ""]) + for item in summary["non_goals"]: + lines.append(f"- {item}") + + return "\n".join(lines).strip() + "\n" + + +def render_reference_scan(skill_dir: Path, references: list[dict], output_md: Path | None = None, output_json: Path | None = None) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "reference-scan.md" + output_json = output_json or reports_dir / "reference-scan.json" + + summary = build_summary(skill_dir, references) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8") + + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a benchmark-oriented reference scan for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--external-reference", action="append", default=[], help="Format: name::category::borrow::avoid") + parser.add_argument("--user-reference", action="append", default=[], help="Format: name::category::borrow::avoid") + parser.add_argument("--local-constraint", action="append", default=[], help="Format: name::category::borrow::avoid") + parser.add_argument("--reference", action="append", default=[], help="Legacy alias for --external-reference.") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + + refs = [parse_reference(item, "external") for item in [*args.external_reference, *args.reference]] + refs.extend(parse_reference(item, "user") for item in args.user_reference) + refs.extend(parse_reference(item, "local") for item in args.local_constraint) + result = render_reference_scan( + Path(args.skill_dir), + refs, + Path(args.output_md).resolve() if args.output_md else None, + Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_reference_synthesis.py b/scripts/render_reference_synthesis.py new file mode 100644 index 0000000..67cdb18 --- /dev/null +++ b/scripts/render_reference_synthesis.py @@ -0,0 +1,552 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path +from typing import Any + +from reference_synthesis_markdown import render_markdown + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +CURATED_TRACKS = [ + { + "source_type": "official", + "name": "Official skill anatomy and context discipline", + "keywords": ["adapter", "portable", "metadata", "description", "references", "context", "entrypoint"], + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + }, + { + "source_type": "official", + "name": "Official workflow product ergonomics", + "keywords": ["quickstart", "review", "viewer", "feedback", "operator", "workflow", "guide"], + "borrow": "Borrow a first-time operator flow that explains itself before it asks for more structure.", + "avoid": "Do not mimic product polish that adds UI bulk without improving clarity.", + }, + { + "source_type": "research", + "name": "Hypothesis-test-learn loop", + "keywords": ["test", "benchmark", "baseline", "compare", "holdout", "optimize", "iteration"], + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + }, + { + "source_type": "research", + "name": "Human-in-the-loop verification", + "keywords": ["review", "audit", "govern", "incident", "compliance", "approval"], + "borrow": "Borrow a review checkpoint wherever trust matters more than raw speed.", + "avoid": "Do not force every skill through heavyweight review when the risk is low.", + }, + { + "source_type": "principles", + "name": "Boundary-first design", + "keywords": ["route", "trigger", "boundary", "exclude", "scope", "near-neighbor"], + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + }, + { + "source_type": "principles", + "name": "Minimum sufficient structure", + "keywords": ["lightweight", "lean", "minimal", "small", "context", "scaffold", "focus"], + "borrow": "Borrow the smallest structure that makes the skill reliable and explainable.", + "avoid": "Do not add files or gates that raise context cost faster than they raise trust.", + }, + { + "source_type": "principles", + "name": "Outcome-backwards design", + "keywords": ["output", "deliverable", "result", "handoff", "keep moving", "packet", "summary"], + "borrow": "Borrow the habit of designing from the required hand-back output backwards.", + "avoid": "Do not start with architecture terms before the deliverable is concrete.", + }, +] + +LIGHTWEIGHT_KEYWORDS = ["light", "lean", "minimal", "small", "simple", "fast", "speed", "quick", "scaffold"] +GOVERNED_KEYWORDS = ["govern", "audit", "review", "approval", "compliance", "risk", "trust", "policy", "cadence"] +POLISH_KEYWORDS = ["polish", "beautiful", "ui", "ux", "experience", "operator flow", "product", "viewer", "ergonomic"] +EVAL_HEAVY_KEYWORDS = ["benchmark", "holdout", "regression", "evidence", "test", "compare", "review checkpoint"] + + +def load_json(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + payload = json.loads(path.read_text(encoding="utf-8")) + return payload if isinstance(payload, dict) else {} + + +def load_manifest(skill_dir: Path) -> dict[str, Any]: + return load_json(skill_dir / "manifest.json") + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def anchor_text(skill_dir: Path, benchmark: dict[str, Any], intent: dict[str, Any]) -> str: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _ = parse_frontmatter(skill_text) + pieces = [ + frontmatter.get("name", skill_dir.name), + frontmatter.get("description", ""), + benchmark.get("query", ""), + intent.get("anchor_sentence", ""), + ] + return " ".join(piece for piece in pieces if piece).lower() + + +def match_keywords(text: str, keywords: list[str]) -> list[str]: + hits = [] + for keyword in keywords: + if keyword in text: + hits.append(keyword) + return hits + + +def select_source_tracks(text: str) -> list[dict[str, Any]]: + grouped: dict[str, list[dict[str, Any]]] = {} + for track in CURATED_TRACKS: + matched = match_keywords(text, track["keywords"]) + score = len(matched) + payload = {**track, "matched_keywords": matched, "score": score} + grouped.setdefault(track["source_type"], []).append(payload) + + selected = [] + for source_type in ("official", "research", "principles"): + candidates = sorted(grouped.get(source_type, []), key=lambda item: item["score"], reverse=True) + chosen = candidates[0] if candidates else None + if chosen is None: + continue + if chosen["score"] == 0: + chosen = {**chosen, "matched_keywords": ["general fit"]} + selected.append( + { + "source_type": source_type, + "name": chosen["name"], + "evidence_mode": "curated-pattern-track", + "matched_keywords": chosen["matched_keywords"], + "borrow": chosen["borrow"], + "avoid": chosen["avoid"], + "why_relevant": ( + f"This track matches: {', '.join(chosen['matched_keywords'])}." + if chosen["matched_keywords"] + else "This track is the best general fit for the current skill shape." + ), + } + ) + return selected + + +def pattern_gate_threshold(manifest: dict[str, Any]) -> int: + tier = str(manifest.get("maturity_tier") or manifest.get("skill_archetype") or "scaffold").lower() + if tier in {"governed", "library"}: + return 4 + if tier == "production": + return 3 + return 2 + + +def score_pattern(candidate: dict[str, Any], source_count: int) -> dict[str, Any]: + text = " ".join( + str(candidate.get(key, "")) + for key in ("name", "borrow", "avoid", "why_relevant", "source_type", "evidence_mode") + ).lower() + gates = { + "recurrence": source_count > 1 + or any(token in text for token in ("repeat", "loop", "workflow", "repositories", "benchmark", "cross")), + "generativity": any( + token in text + for token in ("guide", "loop", "workflow", "pattern", "principle", "operator", "boundary", "output") + ), + "distinctiveness": not any( + phrase in text + for phrase in ("be clear", "be useful", "good quality", "general fit") + ), + "boundary": bool(candidate.get("avoid")) or any(token in text for token in ("avoid", "not", "boundary", "cost")), + } + passed = [name for name, ok in gates.items() if ok] + missing = [name for name, ok in gates.items() if not ok] + return { + "name": candidate.get("name", "Unknown pattern"), + "source_type": candidate.get("source_type", "unknown"), + "borrow": candidate.get("borrow", ""), + "avoid": candidate.get("avoid", ""), + "gates": gates, + "passed": passed, + "missing": missing, + "score": len(passed), + } + + +def build_pattern_gate( + source_tracks: list[dict[str, Any]], + benchmark: dict[str, Any], + user_refs: list[dict[str, Any]], + manifest: dict[str, Any], +) -> dict[str, Any]: + threshold = pattern_gate_threshold(manifest) + source_count = len(source_tracks) + len(benchmark.get("repositories", [])) + len(user_refs) + candidates = [] + for track in source_tracks: + candidates.append(score_pattern(track, source_count)) + for repo in benchmark.get("repositories", [])[:3]: + candidates.append( + score_pattern( + { + "name": repo.get("full_name", "GitHub benchmark"), + "source_type": "github", + "borrow": "; ".join(repo.get("borrow", [])[:2]), + "avoid": "; ".join(repo.get("avoid", [])[:1]), + "why_relevant": "Top GitHub benchmark object with concrete package cues.", + "evidence_mode": "github-benchmark", + }, + source_count, + ) + ) + for reference in user_refs[:3]: + candidates.append( + score_pattern( + { + "name": reference.get("name", "User reference"), + "source_type": "user-reference", + "borrow": reference.get("borrow", ""), + "avoid": reference.get("avoid", ""), + "why_relevant": "User-supplied taste or quality reference.", + "evidence_mode": "user-reference", + }, + source_count, + ) + ) + accepted = [candidate for candidate in candidates if candidate["score"] >= threshold] + deferred = [candidate for candidate in candidates if candidate["score"] < threshold] + return { + "threshold": threshold, + "source_count": source_count, + "accepted": accepted, + "deferred": deferred, + "summary": ( + f"{len(accepted)} accepted, {len(deferred)} deferred using threshold {threshold}/4." + ), + } + + +def unique_items(items: list[str], limit: int) -> list[str]: + seen = set() + output = [] + for item in items: + if item in seen: + continue + seen.add(item) + output.append(item) + if len(output) == limit: + break + return output + + +def join_text(parts: list[str]) -> str: + return " ".join(part for part in parts if part).lower() + + +def has_any(text: str, keywords: list[str]) -> bool: + normalized = re.sub(r"[^a-z0-9]+", " ", text.lower()).strip() + tokens = set(normalized.split()) + for keyword in keywords: + normalized_keyword = re.sub(r"[^a-z0-9]+", " ", keyword.lower()).strip() + if not normalized_keyword: + continue + if " " in normalized_keyword: + if normalized_keyword in normalized: + return True + elif normalized_keyword in tokens: + return True + return False + + +def detect_conflicts( + intent_payload: dict[str, Any], + user_refs: list[dict[str, Any]], + manifest: dict[str, Any], + source_tracks: list[dict[str, Any]], + borrow_now: list[str], + avoid_now: list[str], +) -> list[dict[str, Any]]: + context = intent_payload.get("context", {}) if isinstance(intent_payload, dict) else {} + preference_text = join_text( + [ + str(context.get("job", "")), + str(context.get("primary_output", "")), + str(context.get("description", "")), + " ".join(context.get("constraints", []) or []), + " ".join(context.get("standards", []) or []), + " ".join(context.get("exclusions", []) or []), + str(manifest.get("skill_archetype", "")), + str(manifest.get("maturity_tier", "")), + " ".join( + " ".join( + str(ref.get(key, "")) for key in ("name", "borrow", "avoid", "category") + ) + for ref in user_refs + ), + ] + ) + benchmark_text = join_text( + [ + *borrow_now, + *avoid_now, + *[ + " ".join( + [ + str(track.get("name", "")), + str(track.get("borrow", "")), + str(track.get("avoid", "")), + ] + ) + for track in source_tracks + ], + ] + ) + + conflicts = [] + wants_lightweight = has_any(preference_text, LIGHTWEIGHT_KEYWORDS) + wants_governed = has_any(preference_text, GOVERNED_KEYWORDS) + wants_polish = has_any(preference_text, POLISH_KEYWORDS) + benchmark_heavy = has_any(benchmark_text, GOVERNED_KEYWORDS + EVAL_HEAVY_KEYWORDS) + benchmark_minimal = has_any(benchmark_text, LIGHTWEIGHT_KEYWORDS) + benchmark_anti_polish = any("polish" in item.lower() or "ui bulk" in item.lower() for item in avoid_now) + + if wants_lightweight and benchmark_heavy: + conflicts.append( + { + "key": "lightweight_vs_governance", + "summary": "The stated preference leans lightweight or speed-first, while the benchmark mix leans toward governance, review, or heavier evaluation structure.", + "user_preference": "lightweight or speed-first", + "benchmark_pressure": "governance or evaluation-heavy patterns", + } + ) + if wants_polish and benchmark_anti_polish: + conflicts.append( + { + "key": "polish_vs_minimal_structure", + "summary": "The stated preference leans toward product polish or richer operator experience, while the benchmark recommendation is trying to keep the first pass structurally minimal.", + "user_preference": "product polish or richer operator experience", + "benchmark_pressure": "minimum-structure first pass", + } + ) + if wants_governed and manifest.get("skill_archetype") == "scaffold": + conflicts.append( + { + "key": "governance_vs_scaffold", + "summary": "The stated preference leans toward governance or auditability, but the current archetype is still scaffold-level.", + "user_preference": "governance or auditability", + "benchmark_pressure": "scaffold-first package shape", + } + ) + if wants_governed and benchmark_minimal and not benchmark_heavy: + conflicts.append( + { + "key": "governance_vs_minimal_structure", + "summary": "The stated preference leans toward governance or auditability, while the benchmark recommendation is still biased toward a lightweight first pass.", + "user_preference": "governance or auditability", + "benchmark_pressure": "lightweight first-pass structure", + } + ) + seen = set() + deduped = [] + for conflict in conflicts: + if conflict["key"] in seen: + continue + seen.add(conflict["key"]) + deduped.append(conflict) + return deduped + + +def build_visibility(intent_payload: dict[str, Any], conflicts: list[dict[str, Any]]) -> dict[str, Any]: + reasons = [] + if not intent_payload.get("gate_passed", False): + reasons.append("intent_uncertain") + if conflicts: + reasons.append("design_conflict") + mode = "explicit" if reasons else "silent" + return { + "mode": mode, + "user_decision_required": mode == "explicit", + "reasons": reasons, + "user_note": ( + "Surface the recommendation because intent is still settling or there is a real design conflict that needs a user call." + if mode == "explicit" + else "Apply the synthesis quietly unless uncertainty or a real design conflict appears." + ), + "reviewer_note": "Keep the full benchmark and synthesis evidence visible for authors and reviewers.", + } + + +def build_recommendation( + borrow_now: list[str], + avoid_now: list[str], + intent_payload: dict[str, Any], + visibility: dict[str, Any], + conflicts: list[dict[str, Any]], +) -> dict[str, Any]: + primary_borrow = borrow_now[0] if borrow_now else "Keep the entrypoint lean and boundary-first." + primary_avoid = avoid_now[0] if avoid_now else "Do not add weight that the first pass does not yet need." + if conflicts: + why = f"There is a real design conflict to resolve: {conflicts[0]['summary']}" + elif intent_payload.get("gate_passed", False): + why = "Intent is clear enough, so the system should make the first pattern call quietly." + else: + why = "Intent still has gaps, so the system should surface the recommendation and ask for correction before deepening the package." + return { + "summary": f"Start by borrowing this pattern: {primary_borrow} Avoid this for the first pass: {primary_avoid}", + "borrow_now": borrow_now[:2], + "avoid_for_now": avoid_now[:2], + "why": why, + "user_decision_required": visibility["user_decision_required"], + } + + +def build_summary(skill_dir: Path) -> dict[str, Any]: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _ = parse_frontmatter(skill_text) + benchmark = load_json(skill_dir / "reports" / "github-benchmark-scan.json") + intent_payload = load_json(skill_dir / "reports" / "intent-confidence.json") + reference_scan = load_json(skill_dir / "reports" / "reference-scan.json") + manifest = load_manifest(skill_dir) + + source_tracks = select_source_tracks(anchor_text(skill_dir, benchmark, intent_payload)) + github_repos = benchmark.get("repositories", [])[:3] + github_borrow = benchmark.get("cross_repo", {}).get("borrow", []) + github_avoid = benchmark.get("cross_repo", {}).get("avoid", []) + track_borrow = [track["borrow"] for track in source_tracks] + track_avoid = [track["avoid"] for track in source_tracks] + user_refs = reference_scan.get("user_references", []) + pattern_gate = build_pattern_gate(source_tracks, benchmark, user_refs, manifest) + + borrow_now = unique_items( + [ + *track_borrow, + *github_borrow, + *[ref.get("borrow", "") for ref in user_refs], + ], + 5, + ) + avoid_now = unique_items( + [ + *track_avoid, + *github_avoid, + *[ref.get("avoid", "") for ref in user_refs], + ], + 5, + ) + quality_risers = unique_items( + [ + "Use GitHub repositories for concrete package and workflow patterns.", + "Use curated official or commercial tracks for entrypoint and operator ergonomics.", + "Use research tracks to justify the smallest evaluation loop that still catches regressions.", + "Use principle tracks to keep the package small, boundary-aware, and outcome-driven.", + ], + 4, + ) + conflicts = detect_conflicts(intent_payload, user_refs, manifest, source_tracks, borrow_now, avoid_now) + visibility = build_visibility(intent_payload, conflicts) + recommendation = build_recommendation(borrow_now, avoid_now, intent_payload, visibility, conflicts) + + return { + "skill_name": frontmatter.get("name", skill_dir.name), + "description": frontmatter.get("description", "No description found."), + "intent_confidence": { + "score": intent_payload.get("score", 0), + "band": intent_payload.get("band", "low"), + "gate_passed": intent_payload.get("gate_passed", False), + }, + "github_benchmarks": [ + { + "name": repo.get("full_name"), + "url": repo.get("html_url"), + "stars": repo.get("stars"), + "borrow": repo.get("borrow", [])[:2], + } + for repo in github_repos + ], + "source_tracks": source_tracks, + "synthesis": { + "borrow_now": borrow_now, + "avoid_now": avoid_now, + "quality_risers": quality_risers, + "pattern_gate": pattern_gate, + "conflicts": conflicts, + "recommendation": recommendation, + "visibility": visibility, + "decision_prompt": ( + "Use the recommendation by default. Only surface the underlying benchmark tradeoffs when intent is uncertain or a real design conflict needs a deliberate call." + ), + "source_mix": { + "github_benchmarks": len(github_repos), + "curated_tracks": len(source_tracks), + "user_references": len(user_refs), + }, + }, + } + + +def render_reference_synthesis( + skill_dir: Path, + output_md: 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_md = output_md or reports_dir / "reference-synthesis.md" + output_json = output_json or reports_dir / "reference-synthesis.json" + summary = build_summary(skill_dir) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a multi-source reference synthesis report for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + + result = render_reference_synthesis( + Path(args.skill_dir), + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_regression_history.py b/scripts/render_regression_history.py new file mode 100644 index 0000000..310c209 --- /dev/null +++ b/scripts/render_regression_history.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + + +def load_snapshots(history_dir: Path) -> list[dict]: + snapshots = [] + for path in sorted(history_dir.glob("*.json")): + snapshots.append(json.loads(path.read_text(encoding="utf-8"))) + return snapshots + + +def render_markdown(snapshots: list[dict]) -> str: + lines = [ + "# Regression History", + "", + "| Date | Label | Cases | Families | FP | FN | Route Accuracy | Governance | Governed Examples | Notes |", + "| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |", + ] + for snapshot in snapshots: + summary = snapshot["trigger_summary"] + notes = "; ".join(snapshot.get("notes", [])) + score = snapshot.get("governance_score") + score_display = "-" if score is None else str(score) + route_summary = snapshot.get("route_summary") or {} + route_accuracy = route_summary.get("accuracy") + route_display = "-" if route_accuracy is None else str(route_accuracy) + lines.append( + f"| {snapshot['date']} | {snapshot['label']} | {summary['total_cases']} | {summary['family_count']} | {summary['false_positives']} | {summary['false_negatives']} | {route_display} | {score_display} | {snapshot.get('governed_examples', 0)} | {notes} |" + ) + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render regression history from milestone snapshots.") + parser.add_argument("--history-dir", default="evals/history") + parser.add_argument("--output", default="reports/regression_history.md") + args = parser.parse_args() + + history_dir = Path(args.history_dir) + snapshots = load_snapshots(history_dir) + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(render_markdown(snapshots), encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/scripts/render_review_annotations.py b/scripts/render_review_annotations.py new file mode 100644 index 0000000..19bc6cf --- /dev/null +++ b/scripts/render_review_annotations.py @@ -0,0 +1,402 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +from datetime import date +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_SOURCE = ROOT / "reports" / "review_annotations_input.json" +DEFAULT_OUTPUT_JSON = ROOT / "reports" / "review_annotations.json" +DEFAULT_OUTPUT_MD = ROOT / "reports" / "review_annotations.md" +VALID_GATES = { + "intent-canvas", + "trigger-lab", + "output-lab", + "context-budget", + "runtime-matrix", + "trust-report", + "python-compat", + "architecture-maintainability", + "permission-gates", + "permission-runtime", + "skill-atlas", + "operations-loop", + "review-waivers", + "world-class-evidence", + "registry-audit", + "release-notes", +} +VALID_SEVERITIES = {"info", "note", "warning", "blocker"} +VALID_STATUSES = {"open", "resolved", "deferred"} + + +def display_path(root: Path, path: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +def load_json(path: Path) -> tuple[dict[str, Any], list[str]]: + if not path.exists(): + return {"schema_version": "1.0", "annotations": []}, [] + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + return {"schema_version": "1.0", "annotations": []}, [f"Invalid annotation JSON {display_path(ROOT, path)}: {exc}"] + if not isinstance(payload, dict): + return {"schema_version": "1.0", "annotations": []}, [f"Annotation file root must be an object: {display_path(ROOT, path)}"] + if not isinstance(payload.get("annotations", []), list): + return {"schema_version": "1.0", "annotations": []}, ["annotations must be a list"] + return payload, [] + + +def template_payload() -> dict[str, Any]: + return { + "schema_version": "1.0", + "review_contract": { + "gate_key": "One Review Studio gate key, such as output-lab or trust-report.", + "target_path": "Relative path to the source or report being annotated.", + "line": "Optional positive integer line number.", + "severity": "info | note | warning | blocker.", + "status": "open | resolved | deferred.", + "body": "Reviewer-visible annotation text.", + }, + "annotations": [], + } + + +def annotation_id(annotation: dict[str, Any]) -> str: + if annotation.get("id"): + return str(annotation["id"]) + raw = "|".join( + [ + str(annotation.get("gate_key", "")), + str(annotation.get("target_path", "")), + str(annotation.get("line", "")), + str(annotation.get("body", "")), + ] + ) + return "ann-" + hashlib.sha256(raw.encode("utf-8")).hexdigest()[:12] + + +def source_excerpt(path: Path, line: int | None) -> str: + if line is None or not path.exists() or not path.is_file(): + return "" + try: + lines = path.read_text(encoding="utf-8", errors="replace").splitlines() + except OSError: + return "" + if line < 1 or line > len(lines): + return "" + return lines[line - 1].strip()[:220] + + +def normalize_annotation(skill_dir: Path, raw: dict[str, Any], index: int) -> tuple[dict[str, Any], list[str]]: + failures: list[str] = [] + if not isinstance(raw, dict): + return {}, [f"annotation #{index} must be an object"] + + gate_key = str(raw.get("gate_key", "")).strip() + if gate_key not in VALID_GATES: + failures.append(f"{annotation_id(raw)}: invalid gate_key {gate_key!r}") + + severity = str(raw.get("severity", "note")).strip().lower() + if severity not in VALID_SEVERITIES: + failures.append(f"{annotation_id(raw)}: invalid severity {severity!r}") + + status = str(raw.get("status", "open")).strip().lower() + if status not in VALID_STATUSES: + failures.append(f"{annotation_id(raw)}: invalid status {status!r}") + + body = str(raw.get("body", "")).strip() + if not body: + failures.append(f"{annotation_id(raw)}: body is required") + + target_path = str(raw.get("target_path", "")).strip() + if not target_path: + failures.append(f"{annotation_id(raw)}: target_path is required") + rel = Path(target_path) + if rel.is_absolute(): + failures.append(f"{annotation_id(raw)}: target_path must be relative") + target = skill_dir / "__invalid_absolute_path__" + else: + target = (skill_dir / rel).resolve() + try: + target.relative_to(skill_dir.resolve()) + except ValueError: + failures.append(f"{annotation_id(raw)}: target_path escapes skill directory") + target = skill_dir / "__invalid_escaped_path__" + + line_value = raw.get("line", None) + line: int | None + if line_value is None or line_value == "": + line = None + else: + try: + line = int(line_value) + except (TypeError, ValueError): + line = None + failures.append(f"{annotation_id(raw)}: line must be a positive integer") + else: + if line < 1: + failures.append(f"{annotation_id(raw)}: line must be a positive integer") + + normalized = { + "id": annotation_id(raw), + "gate_key": gate_key, + "target_path": target_path, + "line": line, + "severity": severity, + "status": status, + "reviewer": str(raw.get("reviewer", "")).strip(), + "created_at": str(raw.get("created_at", "")).strip(), + "body": body, + "suggested_action": str(raw.get("suggested_action", "")).strip(), + "evidence": str(raw.get("evidence", "")).strip(), + "target_exists": target.exists(), + "source_excerpt": source_excerpt(target, line), + } + return normalized, failures + + +def append_annotation(source: dict[str, Any], args: argparse.Namespace) -> dict[str, Any]: + annotations = list(source.get("annotations", [])) + raw = { + "id": args.annotation_id, + "gate_key": args.gate_key, + "target_path": args.target_path, + "line": args.line, + "severity": args.severity, + "status": args.status, + "reviewer": args.reviewer, + "created_at": args.created_at or str(date.today()), + "body": args.body, + "suggested_action": args.suggested_action, + "evidence": args.evidence, + } + annotations.append({key: value for key, value in raw.items() if value is not None and value != ""}) + updated = dict(source) + updated["annotations"] = annotations + updated.setdefault("schema_version", "1.0") + return updated + + +def build_summary(annotations: list[dict[str, Any]], failures: list[str]) -> dict[str, Any]: + open_items = [item for item in annotations if item["status"] == "open"] + resolved_items = [item for item in annotations if item["status"] == "resolved"] + deferred_items = [item for item in annotations if item["status"] == "deferred"] + open_blockers = [item for item in open_items if item["severity"] == "blocker"] + open_warnings = [item for item in open_items if item["severity"] == "warning"] + return { + "annotation_count": len(annotations), + "open_count": len(open_items), + "resolved_count": len(resolved_items), + "deferred_count": len(deferred_items), + "open_blocker_count": len(open_blockers), + "open_warning_count": len(open_warnings), + "linked_gate_count": len({item["gate_key"] for item in annotations if item["gate_key"]}), + "target_missing_count": sum(1 for item in annotations if not item["target_exists"]), + "failure_count": len(failures), + } + + +def render_markdown(payload: dict[str, Any]) -> str: + summary = payload["summary"] + lines = [ + "# Review Annotations", + "", + "This report renders reviewer annotations attached to Review Studio gates and source/report paths.", + "", + f"- Annotations: `{summary['annotation_count']}`", + f"- Open: `{summary['open_count']}`", + f"- Resolved: `{summary['resolved_count']}`", + f"- Deferred: `{summary['deferred_count']}`", + f"- Open blockers: `{summary['open_blocker_count']}`", + f"- Open warnings: `{summary['open_warning_count']}`", + "", + ] + if not payload["annotations"]: + lines.extend(["No reviewer annotations recorded yet.", ""]) + else: + lines.extend( + [ + "## Annotation Ledger", + "", + "| ID | Gate | Severity | Status | Target | Reviewer | Note |", + "| --- | --- | --- | --- | --- | --- | --- |", + ] + ) + for item in payload["annotations"]: + line = f":{item['line']}" if item.get("line") else "" + note = item["body"].replace("|", "\\|") + lines.append( + f"| {item['id']} | {item['gate_key']} | {item['severity']} | {item['status']} | " + f"{item['target_path']}{line} | {item.get('reviewer', '')} | {note} |" + ) + if item.get("source_excerpt"): + excerpt = str(item["source_excerpt"]).replace("|", "\\|") + lines.append(f"| | | | | | excerpt | `{excerpt}` |") + if payload.get("failures"): + lines.extend(["", "## Failures", ""]) + for failure in payload["failures"]: + lines.append(f"- {failure}") + lines.extend( + [ + "", + "## Review Rule", + "", + "- Use annotations for reviewer comments tied to a gate or source line.", + "- Use waivers only for explicit acceptance of warning-level release risk.", + "- Open blocker annotations should block a release decision until resolved or deferred with rationale.", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def render_review_annotations( + skill_dir: Path, + source_json: Path, + output_json: Path, + output_md: Path, + args: argparse.Namespace, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + source, failures = load_json(source_json) + template_written = False + should_write_source = False + if args.write_template and not source_json.exists(): + source_json.parent.mkdir(parents=True, exist_ok=True) + source_json.write_text(json.dumps(template_payload(), ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + source = template_payload() + template_written = True + if args.add_annotation: + source = append_annotation(source, args) + should_write_source = True + + annotations: list[dict[str, Any]] = [] + seen = set() + for index, raw in enumerate(source.get("annotations", []), start=1): + normalized, item_failures = normalize_annotation(skill_dir, raw, index) + failures.extend(item_failures) + if normalized: + if normalized["id"] in seen: + failures.append(f"{normalized['id']}: duplicate annotation id") + seen.add(normalized["id"]) + annotations.append(normalized) + + summary = build_summary(annotations, failures) + if should_write_source and not failures: + source_json.parent.mkdir(parents=True, exist_ok=True) + source_json.write_text(json.dumps(source, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + payload = { + "schema_version": "1.0", + "ok": not failures, + "skill_dir": display_path(ROOT, skill_dir), + "source": display_path(skill_dir, source_json), + "summary": summary, + "annotations": annotations, + "failures": failures, + "template_written": template_written, + "artifacts": { + "json": display_path(skill_dir, output_json), + "markdown": display_path(skill_dir, output_md), + }, + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + return payload + + +def render_report( + skill_dir: Path, + annotations_json: Path | None = None, + output_json: Path | None = None, + output_md: Path | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + args = argparse.Namespace( + write_template=False, + add_annotation=False, + annotation_id=None, + gate_key=None, + target_path=None, + line=None, + severity="note", + status="open", + reviewer=None, + created_at=None, + body=None, + suggested_action=None, + evidence=None, + ) + return render_review_annotations( + skill_dir, + annotations_json or skill_dir / "reports" / "review_annotations_input.json", + output_json or skill_dir / "reports" / "review_annotations.json", + output_md or skill_dir / "reports" / "review_annotations.md", + args, + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render reviewer annotations for Review Studio gates and source paths.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--annotations-json") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--write-template", action="store_true") + parser.add_argument("--add-annotation", action="store_true") + parser.add_argument("--annotation-id") + parser.add_argument("--gate-key", choices=sorted(VALID_GATES)) + parser.add_argument("--target-path") + parser.add_argument("--line", type=int) + parser.add_argument("--severity", choices=sorted(VALID_SEVERITIES), default="note") + parser.add_argument("--status", choices=sorted(VALID_STATUSES), default="open") + parser.add_argument("--reviewer") + parser.add_argument("--created-at") + parser.add_argument("--body") + parser.add_argument("--suggested-action") + parser.add_argument("--evidence") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + annotations_json = Path(args.annotations_json).resolve() if args.annotations_json else skill_dir / "reports" / "review_annotations_input.json" + output_json = Path(args.output_json).resolve() if args.output_json else skill_dir / "reports" / "review_annotations.json" + output_md = Path(args.output_md).resolve() if args.output_md else skill_dir / "reports" / "review_annotations.md" + + if args.add_annotation: + missing = [name for name in ("gate_key", "target_path", "body") if not getattr(args, name)] + if missing: + payload = { + "schema_version": "1.0", + "ok": False, + "skill_dir": display_path(ROOT, skill_dir), + "source": display_path(skill_dir, annotations_json), + "summary": build_summary([], missing), + "annotations": [], + "failures": [f"--add-annotation requires {', '.join(missing)}"], + "template_written": False, + "artifacts": { + "json": display_path(skill_dir, output_json), + "markdown": display_path(skill_dir, output_md), + }, + } + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(2) + + payload = render_review_annotations(skill_dir, annotations_json, output_json, output_md, args) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_review_studio.py b/scripts/render_review_studio.py new file mode 100644 index 0000000..f33ce21 --- /dev/null +++ b/scripts/render_review_studio.py @@ -0,0 +1,578 @@ +#!/usr/bin/env python3 +import argparse +import html +import json +from pathlib import Path +from typing import Any + +from review_studio_actions import build_review_actions, render_review_actions +from review_studio_data import evidence_paths, insight_cards, load_review_data +from review_studio_formatting import registry_package_summary, render_gate_contract_panel, render_kv_grid +from review_studio_gates import add_blockers_from_gate, build_gates, gate_contract, weighted_score +from review_studio_layout import render_review_nav, review_studio_css +from review_studio_output_review import render_output_review_section +from review_studio_panels import render_gate_list, render_insights, render_issue_list, render_review_annotations_panel +from review_studio_skillops import render_skillops_section +from review_studio_waivers import render_waiver_candidates_panel +from review_studio_world_class import render_world_class_evidence_entries, render_world_class_intake_checklist + + +ROOT = Path(__file__).resolve().parent.parent + + +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 render_html(report: dict[str, Any]) -> str: + summary = report["summary"] + gates = report["gates"] + gate_details = {item["key"]: item["detail"] for item in 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_html = render_review_nav() + gates_html = render_gate_list(gates) + gate_contract_panel = render_gate_contract_panel(report.get("gate_contract", {})) + metrics_html = render_insights(insights) + blockers_html = render_issue_list("阻断事项", blockers) + warnings_html = render_issue_list("关注事项", warnings) + actions_html = render_review_actions(report["review_actions"]) + annotations_html = render_review_annotations_panel(report["data"].get("review_annotations", {})) + output_summary = report["data"]["output_quality"].get("summary", {}) + output_execution_summary = report["data"]["output_execution"].get("summary", {}) + output_blind_summary = report["data"]["output_blind_review"].get("summary", {}) + output_review_summary = report["data"]["output_review_adjudication"].get("summary", {}) + benchmark_report = report["data"].get("benchmark_reproducibility", {}) + benchmark_summary = benchmark_report.get("summary", {}) + public_claim = benchmark_report.get("public_claim", {}) if isinstance(benchmark_report.get("public_claim", {}), dict) else {} + blueprint_summary = report["data"]["skill_os2_coverage"].get("summary", {}) + conformance_summary = report["data"]["conformance"].get("summary", {}) + compiled_summary = report["data"]["compiled_targets"].get("summary", {}) + trust_summary = report["data"]["trust"].get("summary", {}) + python_compat_summary = report["data"]["python_compatibility"].get("summary", {}) + runtime_permissions_summary = report["data"]["runtime_permissions"].get("summary", {}) + atlas_summary = report["data"]["atlas"].get("summary", {}) + adoption_summary = report["data"]["adoption_drift"].get("summary", {}) + waiver_summary = report["data"]["review_waivers"].get("summary", {}) + world_class_ledger = report["data"].get("world_class_evidence_ledger", {}) + world_class_summary = world_class_ledger.get("summary", {}) + world_class_intake = report["data"].get("world_class_evidence_intake", {}) + world_class_intake_summary = world_class_intake.get("summary", {}) + world_class_claim_summary = report["data"].get("world_class_claim_guard", {}).get("summary", {}) + world_class_entries_html = render_world_class_evidence_entries(world_class_ledger) + world_class_intake_checklist_html = render_world_class_intake_checklist(world_class_intake) + annotation_summary = report["data"]["review_annotations"].get("summary", {}) + annotation_caption = ( + f"{annotation_summary.get('annotation_count', 0)} 条批注;" + f"开放 {annotation_summary.get('open_count', 0)};" + f"阻断 {annotation_summary.get('open_blocker_count', 0)}" + ) + registry_package = report["data"]["registry"].get("package", {}) + package_summary = report["data"]["package_verification"].get("summary", {}) + install_summary = report["data"]["install_simulation"].get("summary", {}) + atlas_panel = render_kv_grid( + atlas_summary, + [ + "skill_count", + "actionable_skill_count", + "actionable_route_collision_count", + "actionable_owner_gap_count", + "actionable_stale_count", + "telemetry_report_count", + "actionable_drift_signal_count", + "drift_signal_count", + "non_actionable_issue_count", + ], + "skill atlas summary missing", + ) + output_panel = render_kv_grid( + output_summary, + ["case_count", "with_skill_pass_rate", "baseline_pass_rate", "delta", "gate_pass", "failure_count"], + "output eval scorecard missing", + ) + execution_panel = render_kv_grid( + output_execution_summary, + [ + "variant_run_count", + "command_executed_count", + "model_executed_count", + "recorded_fixture_count", + "timing_observed_count", + "token_estimated_count", + ], + "output execution report missing", + ) + blind_panel = render_kv_grid( + output_blind_summary, + ["pair_count", "answer_key_separate", "with_skill_hidden_count"], + "blind A/B review pack missing", + ) + review_panel = render_kv_grid( + output_review_summary, + [ + "pair_count", + "judgment_count", + "pending_count", + "agreement_count", + "disagreement_count", + "invalid_decision_count", + "answer_revealed_count", + "pending_answer_hidden_count", + "reviewer_checklist_count", + "reviewer_checklist_pending_count", + ], + "review adjudication report missing", + ) + public_claim_panel = render_kv_grid( + benchmark_summary, + [ + "reproducibility_ready", + "release_lock_ready", + "public_claim_ready", + "public_claim_blocker_count", + "provider_evidence_complete", + "human_review_complete", + "world_class_ready", + ], + "benchmark reproducibility report missing", + ) + public_claim_blocker_rows = public_claim.get("blockers", []) + if public_claim_blocker_rows: + public_claim_blockers_html = ( + "
      " + + "".join( + f"
    • 阻断{html.escape(str(item))}
    • " + for item in public_claim_blocker_rows + ) + + "
    " + ) + else: + public_claim_blockers_html = "

    无公开声明阻断。

    " + output_review_section_html = render_output_review_section(report["data"].get("output_review_adjudication", {})) + blueprint_panel = render_kv_grid( + blueprint_summary, + [ + "item_count", + "module_count", + "recommended_pr_count", + "pass_count", + "warn_count", + "missing_count", + "extension_track_count", + "extension_covered_count", + "extension_partial_count", + "extension_planned_count", + "adaptive_extension_ready", + "local_blueprint_ready", + "public_world_class_ready", + "world_class_evidence_pending_count", + ], + "Skill OS 2.0 blueprint coverage missing", + ) + conformance_panel = render_kv_grid( + conformance_summary, + ["target_count", "pass_count", "fail_count", "warning_count", "failure_count"], + "runtime conformance matrix missing", + ) + compiled_panel = render_kv_grid( + compiled_summary, + ["target_count", "pass_count", "warn_count", "block_count", "failure_count"], + "compiled target report missing", + ) + trust_panel = render_kv_grid( + trust_summary, + ["secret_findings", "script_count", "network_script_count", "help_smoke_failed_count", "package_sha256"], + "security trust report missing", + ) + python_compat_panel = render_kv_grid( + python_compat_summary, + ["target_python", "file_count", "issue_count", "syntax_error_count", "fstring_311_violation_count"], + "python compatibility report missing", + ) + runtime_boundary_panel = render_kv_grid( + runtime_permissions_summary, + [ + "target_count", "pass_count", "native_enforcement_count", "metadata_fallback_count", + "installer_enforcement_pass_count", "installer_permission_failure_count", "residual_risk_count", "failure_count", + ], + "runtime permission probe summary missing", + ) + adoption_panel = render_kv_grid( + adoption_summary, + ["event_count", "adoption_rate", "missed_trigger_count", "bad_output_count", "risk_band"], + "no adoption drift summary", + ) + skillops_section_html = render_skillops_section(report["data"]) + waiver_panel = render_kv_grid( + waiver_summary, + ["waiver_count", "active_count", "expired_count", "invalid_count", "covered_gate_count"], + "no review waiver summary", + ) + waiver_candidates_panel = render_waiver_candidates_panel(report["data"].get("review_waivers", {})) + world_class_panel = render_kv_grid( + world_class_summary, + [ + "ledger_entry_count", + "accepted_count", + "pending_count", + "human_pending_count", + "external_pending_count", + "overclaim_guard_active", + "ready_to_claim_world_class", + ], + "world-class evidence ledger missing", + ) + world_class_intake_panel = render_kv_grid( + world_class_intake_summary, + [ + "schema_present", + "template_count", + "template_pass_count", + "submission_count", + "valid_submission_count", + "invalid_submission_count", + "ready_for_external_collection", + "ready_for_ledger_review", + "ready_to_claim_world_class", + ], + "world-class evidence intake missing", + ) + world_class_claim_panel = render_kv_grid( + world_class_claim_summary, + [ + "ledger_ready_to_claim_world_class", + "ledger_pending_count", + "claim_surface_count", + "violation_count", + "overclaim_guard_active", + ], + "world-class claim guard missing", + ) + registry_panel = render_kv_grid( + registry_package_summary(registry_package), + [ + "name", + "version", + "maturity", + "owner", + "license", + "trust_level", + "targets", + "compatibility_pass_count", + "archive_sha256", + ], + "registry package metadata missing", + ) + package_panel = render_kv_grid( + package_summary, + ["target_count", "adapter_count", "archive_present", "archive_entry_count", "failure_count", "warning_count", "archive_sha256"], + "package verification missing", + ) + install_panel = render_kv_grid( + install_summary, + [ + "archive_extracted", + "entrypoint_loaded", + "manifest_loaded", + "interface_loaded", + "adapter_count", + "installer_permission_enforced_count", + "installer_permission_failure_count", + "permission_target_count", + "permission_capability_count", + "failure_count", + "warning_count", + ], + "install simulation missing", + ) + 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}
    +
    + +
    +

    闸门契约

    +
    {gate_contract_panel}
    +
    + +
    + {blockers_html} + {warnings_html} +
    + +
    +

    修复动作

    +
    {actions_html}
    +
    + +
    +

    审查批注

    +

    当前批注:{html.escape(annotation_caption)}

    +
    {annotations_html}
    +
    + +
    +
    +

    意图画布

    +

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

    +
    +
    +

    证据路径

    +
      {evidence_html}
    +
    +
    + +
    +

    触发实验

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

    +

    组合治理

    {atlas_panel}
    +
    + +
    +

    输出实验

    {output_panel}
    +

    执行证据

    {execution_panel}
    +
    + +
    +

    盲评包

    {blind_panel}
    +

    审定报告

    {review_panel}
    +
    + + {output_review_section_html} +
    +

    发布标准

    Governed 和 Library 至少需要 5 个 output eval cases,并覆盖 file-backed、near-neighbor、boundary case、execution evidence 和 blind A/B review pack。

    +

    人工结论

    没有 reviewer 决策时只显示 pending;只有真实决策文件会进入一致率和分歧统计。

    +
    + +
    +

    评审方式

    先打开 reports/output_review_kit.html 做盲评,填入 reports/output_review_decisions.json,再用 reports/output_review_adjudication.md 核对答案 key。

    +

    运行方式

    reports/output_execution_runs.md 会标明 recorded fixture、command run 或 model run;只有 provider runner 返回 model metadata 时才算 model-executed。

    +
    + +
    +

    运行矩阵

    {conformance_panel}
    +

    目标编译

    {compiled_panel}
    +
    + +
    +

    上下文

    {html.escape(gate_details.get('context-budget', 'context budget missing'))}

    +

    编译证据

    Review reports/compiled_targets.md before packaging to inspect target adapter modes, generated files, preserved semantics, warnings, and unsupported features.

    +
    + +
    +

    信任报告

    {trust_panel}
    +

    安全边界

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

    +
    + +
    +

    Python 兼容

    {python_compat_panel}
    +

    解释器边界

    CI 和发布审查以 Python 3.11 兼容为底线;本地更高版本允许的新语法不能绕过兼容门禁。

    +
    + +
    +

    权限批准

    {html.escape(gate_details.get('permission-gates', 'permission governance missing'))}

    +

    批准策略

    高权限能力需要 reviewer、scope、reason、expires_at 和 openai/claude/generic 目标端 enforcement 说明。

    +
    + +
    +

    权限探针

    {html.escape(gate_details.get('permission-runtime', 'runtime permission probes missing'))}

    +

    运行边界

    {runtime_boundary_panel}
    +
    + +
    +

    组合治理

    {html.escape(gate_details.get('skill-atlas', 'skill atlas missing'))}

    +

    下一动作

    优先处理真实 portfolio 中的 duplicate names、stale skills、owner gaps,再用运营回路判断真实影响。

    +
    + +
    +

    运营回路

    {html.escape(gate_details.get('operations-loop', 'adoption drift report missing'))}

    +

    漂移信号

    {adoption_panel}
    +
    + + {skillops_section_html} + +
    +

    人工批准

    +

    warning 可以被有边界地接受,但必须写入 reviewer、理由、范围和到期时间;blocker 与 world-class 完成证据不能通过 waiver 变成通过。

    +
    +

    批准概况

    {html.escape(gate_details.get('review-waivers', 'review waiver ledger missing'))}

    +

    批准台账

    {waiver_panel}
    +
    +

    批准候选

    + {waiver_candidates_panel} +
    + +
    +

    世界证据

    +

    这里列出每个 world-class 证据项的当前状态、完成定义、证据来源、隐私约束和下一步;计划、metadata fallback、待评审和本地命令不会被当成完成证据。

    + {world_class_entries_html} +
    + +
    +

    提交清单

    +

    每张卡片给出模板、提交文件、准备命令、校验命令、收集要求、通过条件和隐私边界;只有真实 provider、真人、原生权限或真实客户端证据通过 intake 后,才进入 ledger review。

    + {world_class_intake_checklist_html} +
    + +
    +

    蓝图覆盖

    {blueprint_panel}
    +

    覆盖边界

    蓝图覆盖只证明 2.0 模块、建议 PR、脚本、报告和测试在本地闭环;public world-class 仍以 world-class evidence ledger 的真人和外部证据为准。

    +
    + +
    +

    公开声明

    {public_claim_panel}
    +

    声明阻断

    {public_claim_blockers_html}
    +
    + +
    +

    世界证据

    {html.escape(gate_details.get('world-class-evidence', 'world-class evidence ledger missing'))}

    +

    证据台账

    {world_class_panel}
    +
    + +
    +

    证据入口

    {world_class_intake_panel}
    +

    入口边界

    intake 只校验证据包格式、来源、隐私和反过度声明;只有 ledger 看到真实 provider、真人、原生权限或真实客户端结果后,world-class 才能进入完成审计。

    +
    + +
    +

    声明守卫

    {world_class_claim_panel}
    +

    声明边界

    claim guard 扫描 README、docs 和 reports 中的完成态表述;ledger 未 ready 时,任何英文完成断言、true 状态声明或中文完成态都会阻断发布审查。

    +
    + +
    +

    注册审计

    {html.escape(gate_details.get('registry-audit', 'registry audit missing'))}

    +

    包体元数据

    {registry_panel}
    +
    + +
    +

    发布路线

    {html.escape(gate_details.get('release-notes', 'release notes missing'))}

    +

    包体验证

    {package_panel}
    +
    + +
    +

    安装模拟

    {install_panel}
    +

    权限覆盖

    安装模拟会读取解压后的 target adapter,并按 declared_capabilities 核对 security/permission_policy.json 中的有效批准、证据、过期时间和目标端 enforcement 说明。

    +
    +
    + + +""" + + +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) + contract = gate_contract(gates) + blockers, warnings = add_blockers_from_gate(gates) + review_actions = build_review_actions(gates, skill_dir, output_html, data) + action_by_gate = {str(item.get("gate_key", "")): item for item in review_actions} + for gate in gates: + action = action_by_gate.get(str(gate.get("key", ""))) + gate["review_action_id"] = str(action.get("action_id", "")) if action else "" + gate["review_action_status"] = str(action.get("status", "")) if action else "" + gate["review_action_title"] = str(action.get("title", "")) if action else "" + gate["review_action_next_step"] = str(action.get("next_step", "")) if action else "" + gate["review_action_reason"] = str(action.get("reason", "")) if action else "" + gate["review_action_source_ref_count"] = len(action.get("source_refs", [])) if action else 0 + gate["review_action_verification_command"] = str(action.get("verification_command", "")) if action else "" + score = weighted_score(gates) + annotation_summary = data["review_annotations"].get("summary", {}) + open_annotation_blockers = int(annotation_summary.get("open_blocker_count", 0) or 0) + open_annotation_warnings = int(annotation_summary.get("open_warning_count", 0) or 0) + decision = "blocked" if blockers or open_annotation_blockers else ("review" if warnings or open_annotation_warnings else "ready") + report = { + "schema_version": "2.0", + "ok": contract["ok"], + "skill_dir": display_path(skill_dir, skill_dir), + "summary": { + "decision": decision, + "world_class_score": score, + "gate_count": len(gates), + "gate_contract_ok": contract["ok"], + "blocker_count": len(blockers), + "warning_count": len(warnings), + "action_count": len(review_actions), + "annotation_count": int(annotation_summary.get("annotation_count", 0) or 0), + "open_annotation_count": int(annotation_summary.get("open_count", 0) or 0), + "open_annotation_blocker_count": open_annotation_blockers, + "open_annotation_warning_count": open_annotation_warnings, + }, + "gates": gates, + "gate_contract": contract, + "blockers": blockers, + "warnings": warnings, + "review_actions": review_actions, + "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() + 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 + payload = render_review_studio(Path(args.skill_dir), output_html=output_html, output_json=output_json) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + +if __name__ == "__main__": + main() diff --git a/scripts/render_review_viewer.py b/scripts/render_review_viewer.py new file mode 100644 index 0000000..8383202 --- /dev/null +++ b/scripts/render_review_viewer.py @@ -0,0 +1,517 @@ +#!/usr/bin/env python3 +import argparse +import html +import json +from pathlib import Path + +from review_viewer_data import ( + architecture_steps, + benchmark_cards, + compare_rows, + ensure_report_inputs, + evidence_readiness, + synthesis_cards, + variant_diff_cards, +) + + +ASSETS_DIR = Path(__file__).resolve().parent.parent / "assets" + + +def review_viewer_css() -> str: + return (ASSETS_DIR / "review-viewer.css").read_text(encoding="utf-8").strip() + + +def render_html(report: dict) -> str: + overview = report["overview"] + intent = report["intent"] + intent_confidence = report.get("intent_confidence", {}) + reference = report["reference"] + iteration = report["iteration"] + directions = iteration.get("directions", [])[:3] + feedback = report.get("feedback", {}) + baseline = report.get("baseline", {}) + compare = report.get("compare", {}) + promotion = report.get("promotion", {}) + benchmark = report.get("benchmark", {}) + reference_synthesis = report.get("reference_synthesis", {}) + output_risk = report.get("output_risk", {}) + artifact_design = report.get("artifact_design", {}) + prompt_quality = report.get("prompt_quality", {}) + architecture = architecture_steps(overview) + compare_table_rows = compare_rows(compare) + benchmark_rows = benchmark_cards(benchmark) + synthesis_rows = synthesis_cards(reference_synthesis) + variant_cards = variant_diff_cards(compare) + readiness = evidence_readiness(report) + + strength_items = "".join(f"
  • {html.escape(item)}
  • " for item in overview.get("strengths", [])) + logic_items = "".join(f"
  • {html.escape(item)}
  • " for item in overview.get("logic_steps", [])) + usage_items = "".join(f"
  • {html.escape(item)}
  • " for item in overview.get("usage_steps", [])) + question_items = "".join( + f"
  • {html.escape(item['question'])}
    {html.escape(item['why'])}
  • " + for item in intent.get("questions", [])[:5] + ) + reference_items = "".join( + ( + f"
  • {html.escape(item['name'])} · {html.escape(item['category'])}
    " + f"Borrow: {html.escape(item['borrow'])}
    " + f"Avoid: {html.escape(item['avoid'])}
  • " + ) + for item in reference.get("external_references", [])[:4] + ) + if not reference_items: + reference_items = "
  • No external benchmark objects recorded yet. Add 2 to 5 references before deepening the package.
  • " + + output_risk_items = "".join( + ( + "
  • " + f"{html.escape(item.get('label', item.get('key', 'Risk')))}
    " + f"{html.escape('; '.join(item.get('risks', [])[:2]))}" + "
  • " + ) + for item in output_risk.get("risk_families", [])[:3] + ) + if not output_risk_items: + output_risk_items = "
  • No output risk profile attached yet. Generate one before approving example outputs.
  • " + + artifact_design_items = "".join( + ( + "
  • " + f"{html.escape(item.get('label', item.get('key', 'Artifact')))}
    " + f"{html.escape(item.get('direction', ''))}" + "
  • " + ) + for item in artifact_design.get("artifact_families", [])[:3] + ) + if not artifact_design_items: + artifact_design_items = "
  • No artifact design profile attached yet. Generate one before approving visual or document outputs.
  • " + design_gate_items = "".join( + f"
  • {html.escape(item)}
  • " for item in artifact_design.get("quality_gates", [])[:5] + ) or "
  • No artifact design quality gates attached yet.
  • " + + prompt_quality_items = "".join( + ( + "
  • " + f"{html.escape(item.get('label', item.get('key', 'Quality')))} · " + f"{html.escape(str(item.get('score', 'n/a')))} / 100
    " + f"{html.escape(item.get('repair', ''))}" + "
  • " + ) + for item in prompt_quality.get("quality_matrix", [])[:5] + ) or "
  • No prompt quality profile attached yet.
  • " + rtf_items = "".join( + ( + "
  • " + f"{html.escape(key.title())}
    " + f"{html.escape(str(value))}" + "
  • " + ) + for key, value in prompt_quality.get("rtf_to_skill", {}).items() + ) or "
  • No RTF mapping attached yet.
  • " + + readiness_html = "".join( + ( + "
  • " + f"{html.escape(item['label'])} · {html.escape(item['status'])}
    " + f"{html.escape(item['detail'])}" + "
  • " + ) + for item in readiness["checks"] + ) + + direction_cards = "".join( + ( + "
    " + f"

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

    " + f"

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

    " + "
      " + + "".join(f"
    • {html.escape(action)}
    • " for action in item.get("actions", [])[:3]) + + "
    " + f"
    Unlocks: {html.escape(item['unlocks'])}
    " + "
    " + ) + for item in directions + ) + + architecture_html = "".join( + ( + "
    " + f"
    {html.escape(item['label'])}
    " + f"
    {html.escape(item['detail'])}
    " + "
    " + ) + for item in architecture + ) + + feedback_entries = feedback.get("entries", [])[-3:] + feedback_html = "" + if feedback_entries: + feedback_html = "".join( + ( + "
  • " + f"{html.escape(entry.get('category', 'general'))} · " + f"rating {html.escape(str(entry.get('rating', 'n/a')))}
    " + f"{html.escape(entry.get('note', ''))}" + "
  • " + ) + for entry in reversed(feedback_entries) + ) + else: + feedback_html = "
  • No lightweight feedback captured yet. Use `yao.py feedback` to record quick review notes.
  • " + + baseline_html = "" + if compare_table_rows: + compare_rows_html = "".join( + f"{html.escape(item['label'])}{html.escape(str(item['tokens']))}{html.escape(str(item['dev_errors']))}{html.escape(str(item['holdout_errors']))}{html.escape(item['strategy'])}" + for item in compare_table_rows + ) + selection_logic = compare.get("selection_logic", "Choose the smallest candidate that improves routing without adding unnecessary weight.") + winner_label = compare.get("summary", {}).get("winner_label", compare.get("winner", {}).get("label", "Current")) + baseline_html = ( + "
    " + f"

    Winner: {html.escape(str(winner_label))}

    " + f"

    {html.escape(str(selection_logic))}

    " + "" + f"{compare_rows_html}
    VariantTokensDev ErrorsHoldout ErrorsStrategy
    " + "
    " + ) + elif baseline: + summary = baseline.get("summary", {}) + baseline_html = ( + "
    " + f"

    Targets: {html.escape(str(summary.get('target_count', 0)))}

    " + f"

    Baseline errors: {html.escape(str(summary.get('baseline_total_errors', 0)))}

    " + f"

    Current errors: {html.escape(str(summary.get('current_total_errors', 0)))}

    " + f"

    Winner errors: {html.escape(str(summary.get('winner_total_errors', 0)))}

    " + "
    " + ) + else: + baseline_html = "

    No baseline comparison has been recorded for this package yet.

    " + + promotion_html = "" + if promotion: + summary = promotion.get("summary", {}) + promotion_html = ( + "
    " + f"

    Promote: {html.escape(str(summary.get('promote', 0)))}

    " + f"

    Keep current: {html.escape(str(summary.get('keep_current', 0)))}

    " + f"

    Blocked: {html.escape(str(summary.get('blocked', 0)))}

    " + "
    " + ) + else: + promotion_html = "

    No promotion summary is attached to this package yet.

    " + + benchmark_html = "" + if benchmark_rows: + benchmark_html = "".join( + ( + "
    " + f"

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

    " + "

    Borrow now

    " + + ("
      " + "".join(f"
    • {html.escape(borrow)}
    • " for borrow in item.get('borrow', [])) + "
    " if item.get("borrow") else "

    No borrow cues recorded.

    ") + + "

    Avoid

    " + + ("
      " + "".join(f"
    • {html.escape(avoid)}
    • " for avoid in item.get('avoid', [])) + "
    " if item.get("avoid") else "

    No avoid cues recorded.

    ") + + "
    " + ) + for item in benchmark_rows + ) + else: + benchmark_html = "

    No GitHub benchmark scan has been attached to this package yet.

    " + + synthesis_html = "" + if synthesis_rows: + synthesis_html = "".join( + ( + "
    " + f"

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

    " + "

    Borrow now

    " + + ("
      " + "".join(f"
    • {html.escape(borrow)}
    • " for borrow in item.get('borrow', [])) + "
    " if item.get("borrow") else "

    No borrow cues recorded.

    ") + + "

    Avoid

    " + + ("
      " + "".join(f"
    • {html.escape(avoid)}
    • " for avoid in item.get('avoid', [])) + "
    " if item.get("avoid") else "

    No avoid cues recorded.

    ") + + "
    " + ) + for item in synthesis_rows + ) + else: + synthesis_html = "

    No multi-source synthesis has been generated yet.

    " + + variant_diff_html = "" + if variant_cards: + variant_diff_html = "".join( + ( + "
    " + f"

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

    {html.escape(item['strategy'])}
    " + f"

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

    " + "
    " + f"tokens {html.escape(str(item['tokens']))} ({html.escape(item['token_delta'])})" + f"dev {html.escape(str(item['dev_errors']))} ({html.escape(item['dev_delta'])})" + f"holdout {html.escape(str(item['holdout_errors']))} ({html.escape(item['holdout_delta'])})" + "
    " + "
    " + "

    Adds relative to baseline

    " + + ( + "
      " + "".join(f"
    • {html.escape(value)}
    • " for value in item["added"]) + "
    " + if item["added"] + else "

    No added cues.

    " + ) + + "

    Drops from baseline

    " + + ( + "
      " + "".join(f"
    • {html.escape(value)}
    • " for value in item["removed"]) + "
    " + if item["removed"] + else "

    No dropped cues.

    " + ) + + "
    " + ) + for item in variant_cards + ) + else: + variant_diff_html = "

    No description optimization compare payload is attached yet.

    " + + return f""" + + + + + {html.escape(overview.get('display_name', overview.get('title', 'Skill Review')))} Review Viewer + + + +
    +
    +

    Review Viewer

    +

    {html.escape(overview.get('display_name', overview.get('title', 'Skill Review')))}

    +

    {html.escape(overview.get('description', ''))}

    +
    + maturity: {html.escape(str(overview.get('metadata', {}).get('maturity_tier', 'scaffold')))} + archetype: {html.escape(str(overview.get('metadata', {}).get('skill_archetype', 'scaffold')))} + format: {html.escape(str(overview.get('metadata', {}).get('canonical_format', 'agent-skills')))} + updated: {html.escape(str(overview.get('metadata', {}).get('updated_at', 'n/a')))} + intent confidence: {html.escape(str(intent_confidence.get('score', 'n/a')))} / 100 +
    +
    + +
    +

    Architecture at a glance

    +
    {architecture_html}
    +
    + +
    +
    +

    Core logic

    +
      {logic_items}
    +
    +
    +

    How to use it

    +
      {usage_items}
    +
    +
    + +
    +
    +

    Intent questions

    +
      {question_items}
    +
    +
    +

    Why this package is strong

    +
      {strength_items}
    +
    +
    + +
    +
    +

    Borrow plan

    +
      {reference_items}
    +
    +
    +

    Compare view

    + {baseline_html} +
    +
    + +
    +

    Variant diff studio

    +
    {variant_diff_html}
    +
    + +
    +
    +

    Evidence readiness

    +

    Readiness score: {html.escape(str(readiness['score']))}/100

    +
      {readiness_html}
    +
    +
    +

    Honest boundary check

    +
      +
    • Are the known limits visible before the package deepens?
    • +
    • Does the evidence support the borrowed patterns?
    • +
    • Should uncertainty become a clarification question instead of more structure?
    • +
    +
    +
    + +
    +
    +

    Output risk profile

    +
      {output_risk_items}
    +
    +
    +

    Self-repair checks

    +
      {"".join(f"
    • {html.escape(item)}
    • " for item in output_risk.get('self_repair_checks', [])[:5]) or "
    • No self-repair checks attached yet.
    • "}
    +
    +
    + +
    +
    +

    Artifact design profile

    +

    Design system: {html.escape(str(artifact_design.get('design_system', 'not generated')))}

    +
      {artifact_design_items}
    +
    +
    +

    Visual quality gates

    +
      {design_gate_items}
    +
    +
    + +
    +
    +

    Prompt quality profile

    +

    Relevance: {html.escape(str(prompt_quality.get('relevance', 'not generated')))} · score {html.escape(str(prompt_quality.get('overall_quality_score', 'n/a')))} / 100 · complexity {html.escape(str(prompt_quality.get('complexity', {}).get('band', 'n/a')))}

    +
      {prompt_quality_items}
    +
    +
    +

    RTF to skill mapping

    +
      {rtf_items}
    +
    +
    + +
    +
    +

    Reference coach

    +
    {benchmark_html}
    +
    +
    +

    Decide before you deepen

    +
      +
    • Choose one pattern to borrow on purpose, not three at once.
    • +
    • State one thing this skill will not inherit from the benchmark objects.
    • +
    • Only deepen the package after that choice is visible in the boundary or execution flow.
    • +
    +
    +
    + +
    +
    +

    Reference synthesis

    +
    {synthesis_html}
    +
    +
    +

    Borrow now

    +
      {"".join(f"
    • {html.escape(item)}
    • " for item in reference_synthesis.get('synthesis', {}).get('borrow_now', [])[:4]) or "
    • No synthesis borrow cues recorded yet.
    • "}
    +

    {html.escape(reference_synthesis.get('synthesis', {}).get('decision_prompt', 'Run the reference synthesis after the benchmark scan to decide what to borrow next.'))}

    +
    +
    + +
    +

    Top three next moves

    +
    {direction_cards}
    +
    + +
    +
    +

    Recent feedback

    +
      {feedback_html}
    +
    +
    +

    Promotion status

    + {promotion_html} +
    +
    + +
    +
    +

    Package map

    +
      {"".join(f"
    • {html.escape(item['path'])} — {html.escape(item['label'])}
    • " for item in overview.get('package_map', [])[:8])}
    +
    +
    +

    First-pass review frame

    +
      +
    • Does the trigger stay narrow enough for the intended job?
    • +
    • Does the archetype match the real reuse level?
    • +
    • Are we adding structure faster than we are adding reliability?
    • +
    • Should the next step be trigger tightening, execution assets, or portability hardening?
    • +
    +
    +
    + +
    +
    +

    Authoring discipline

    +
      +
    • Name unresolved assumptions before deepening the package.
    • +
    • Keep the package no larger than the recurring job requires.
    • +
    • Touch only files that directly support the requested change.
    • +
    • Tie every meaningful new artifact to a check or reviewer note.
    • +
    +
    +
    +

    Reviewer guardrails

    +
      +
    • Block speculative features that are not backed by real workflow variation.
    • +
    • Move unverifiable ideas into next-step candidates instead of baseline structure.
    • +
    • Reject decorative folders, reports, or governance that do not reduce risk.
    • +
    • Ask for one high-leverage clarification when job, output, or exclusion is still fuzzy.
    • +
    +
    +
    +
    + + +""" + + +def render_review_viewer(skill_dir: Path, output_html: Path | None = None, output_json: Path | None = None) -> dict: + 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-viewer.html" + output_json = output_json or reports_dir / "review-viewer.json" + + report = ensure_report_inputs(skill_dir) + report["evidence_readiness"] = evidence_readiness(report) + output_html.write_text(render_html(report), encoding="utf-8") + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "html": str(output_html), + "json": str(output_json), + }, + "summary": { + "title": report["overview"].get("title", skill_dir.name), + "maturity_tier": report["overview"].get("metadata", {}).get("maturity_tier", "scaffold"), + "directions": len(report["iteration"].get("directions", [])), + "feedback_entries": len(report.get("feedback", {}).get("entries", [])), + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a compact HTML review viewer 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() + result = render_review_viewer( + 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(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_review_waivers.py b/scripts/render_review_waivers.py new file mode 100644 index 0000000..eb13cff --- /dev/null +++ b/scripts/render_review_waivers.py @@ -0,0 +1,395 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +from datetime import date, timedelta +from pathlib import Path +from typing import Any + +from review_studio_gates import REVIEW_STUDIO_GATE_KEYS + + +ROOT = Path(__file__).resolve().parent.parent +NON_WAIVABLE_GATE_KEYS = {"review-waivers", "world-class-evidence"} +WAIVERABLE_GATE_KEYS = REVIEW_STUDIO_GATE_KEYS - NON_WAIVABLE_GATE_KEYS +KNOWN_GATE_KEYS = WAIVERABLE_GATE_KEYS +VALID_DECISIONS = {"accepted-risk", "false-positive", "temporary-exception"} +MIN_REASON_CHARS = 20 + + +def display_path(path: Path) -> str: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +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 today_from(value: str | None) -> date: + if not value: + return date.today() + return date.fromisoformat(value[:10]) + + +def parse_date(value: str) -> date | None: + try: + return date.fromisoformat(value[:10]) + except (TypeError, ValueError): + return None + + +def waiver_id(entry: dict[str, Any]) -> str: + raw = "|".join( + [ + str(entry.get("gate_key", "")), + str(entry.get("reviewer", "")), + str(entry.get("created_at", "")), + str(entry.get("reason", "")), + ] + ) + return hashlib.sha1(raw.encode("utf-8")).hexdigest()[:12] + + +def normalize_waiver(entry: dict[str, Any]) -> dict[str, Any]: + normalized = { + "id": str(entry.get("id") or ""), + "gate_key": str(entry.get("gate_key") or ""), + "decision": str(entry.get("decision") or "accepted-risk"), + "reviewer": str(entry.get("reviewer") or ""), + "reason": str(entry.get("reason") or ""), + "created_at": str(entry.get("created_at") or ""), + "expires_at": str(entry.get("expires_at") or ""), + "evidence": str(entry.get("evidence") or ""), + "scope": str(entry.get("scope") or "current-release"), + } + normalized["id"] = normalized["id"] or waiver_id(normalized) + return normalized + + +def add_waiver(existing: list[dict[str, Any]], args: argparse.Namespace, today: date) -> list[dict[str, Any]]: + created_at = args.created_at or today.isoformat() + entry = normalize_waiver( + { + "gate_key": args.gate_key, + "decision": args.decision, + "reviewer": args.reviewer, + "reason": args.reason, + "created_at": created_at, + "expires_at": args.expires_at, + "evidence": args.evidence or "", + "scope": args.scope, + } + ) + return [*existing, entry] + + +def validate_waivers(waivers: list[dict[str, Any]], today: date) -> tuple[list[dict[str, Any]], list[str], list[str]]: + normalized = [] + failures = [] + warnings = [] + seen_ids = set() + + for index, raw in enumerate(waivers, start=1): + entry = normalize_waiver(raw) + entry_failures = [] + entry_warnings = [] + if entry["id"] in seen_ids: + entry_failures.append("duplicate waiver id") + seen_ids.add(entry["id"]) + if entry["gate_key"] not in KNOWN_GATE_KEYS: + entry_failures.append(f"unknown gate_key: {entry['gate_key'] or ''}") + if entry["decision"] not in VALID_DECISIONS: + entry_failures.append(f"invalid decision: {entry['decision'] or ''}") + if not entry["reviewer"]: + entry_failures.append("reviewer is required") + if len(entry["reason"]) < MIN_REASON_CHARS: + entry_failures.append(f"reason must be at least {MIN_REASON_CHARS} characters") + created_at = parse_date(entry["created_at"]) + if created_at is None: + entry_failures.append("created_at must be ISO date") + expires_at = parse_date(entry["expires_at"]) + if expires_at is None: + entry_failures.append("expires_at must be ISO date") + elif expires_at < today: + entry_warnings.append("waiver is expired") + entry["status"] = "invalid" if entry_failures else ("expired" if entry_warnings else "active") + entry["validation"] = {"failures": entry_failures, "warnings": entry_warnings} + normalized.append(entry) + failures.extend([f"waiver {index} ({entry['id']}): {item}" for item in entry_failures]) + warnings.extend([f"waiver {index} ({entry['id']}): {item}" for item in entry_warnings]) + + return normalized, failures, warnings + + +def output_lab_candidate(skill_dir: Path, covered_gate_keys: set[str], today: date) -> dict[str, Any] | None: + output_quality = load_json(skill_dir / "reports" / "output_quality_scorecard.json").get("summary", {}) + output_execution = load_json(skill_dir / "reports" / "output_execution_runs.json").get("summary", {}) + output_review = load_json(skill_dir / "reports" / "output_review_adjudication.json").get("summary", {}) + if not output_quality and not output_execution and not output_review: + return None + pending = int(output_review.get("pending_count", 0) or 0) + model_executed = int(output_execution.get("model_executed_count", 0) or 0) + failure_count = int(output_quality.get("failure_count", 0) or 0) + if pending == 0 and model_executed > 0 and failure_count == 0: + return None + status = "covered" if "output-lab" in covered_gate_keys else "needs-reviewer-decision" + return { + "gate_key": "output-lab", + "label": "Output Lab", + "status": status, + "waiver_allowed": True, + "decision_options": sorted(VALID_DECISIONS), + "risk_summary": ( + f"review pending {pending}; model-executed {model_executed}; " + f"output failures {failure_count}" + ), + "required_review": [ + "Reviewer confirms this release does not claim provider-backed or human-adjudicated output superiority.", + "Reviewer names the release scope and expiry date.", + "Reviewer links output_review_adjudication or output_execution evidence.", + ], + "suggested_evidence": "reports/output_review_adjudication.md", + "suggested_command": ( + "python3 scripts/yao.py review-waivers . --add-waiver " + "--gate-key output-lab --reviewer \"\" " + "--reason \"Output Lab has pending human/provider evidence; accepted only for this bounded review scope.\" " + f"--expires-at {(today + timedelta(days=365)).isoformat()} " + "--evidence reports/output_review_adjudication.md" + ), + "world_class_boundary": "Does not count as provider, human, or public world-class completion evidence.", + } + + +def world_class_boundary(skill_dir: Path) -> dict[str, Any] | None: + ledger_summary = load_json(skill_dir / "reports" / "world_class_evidence_ledger.json").get("summary", {}) + if not ledger_summary: + return None + pending = int(ledger_summary.get("pending_count", 0) or 0) + if pending == 0 and ledger_summary.get("ready_to_claim_world_class") is True: + return None + return { + "gate_key": "world-class-evidence", + "label": "World-Class Evidence", + "status": "cannot-waive", + "waiver_allowed": False, + "risk_summary": ( + f"{pending} pending evidence entries; " + f"{ledger_summary.get('human_pending_count', 0)} human pending; " + f"{ledger_summary.get('external_pending_count', 0)} external pending" + ), + "required_review": [ + "Do not use a waiver to claim public world-class readiness.", + "Either submit accepted ledger evidence or state that this release does not claim world-class completion.", + "Keep claim guard active until ledger summary.ready_to_claim_world_class is true.", + ], + "suggested_evidence": "reports/world_class_evidence_ledger.md", + "suggested_command": ( + "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions " + "&& python3 scripts/yao.py world-class-claim-guard ." + ), + "world_class_boundary": "Non-waivable completion boundary.", + } + + +def build_waiver_candidates(skill_dir: Path, covered_gate_keys: list[str], today: date) -> list[dict[str, Any]]: + covered = set(covered_gate_keys) + candidates = [] + output_candidate = output_lab_candidate(skill_dir, covered, today) + if output_candidate: + candidates.append(output_candidate) + world_boundary = world_class_boundary(skill_dir) + if world_boundary: + candidates.append(world_boundary) + return candidates + + +def render_report( + skill_dir: Path, + waivers_json: Path | None = None, + output_json: Path | None = None, + output_md: Path | None = None, + generated_at: str | None = None, + add_args: argparse.Namespace | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + reports = skill_dir / "reports" + reports.mkdir(parents=True, exist_ok=True) + output_json = output_json or reports / "review_waivers.json" + output_md = output_md or reports / "review_waivers.md" + source_json = waivers_json or output_json + today = today_from(generated_at) + payload = load_json(source_json) + raw_waivers = payload.get("waivers", []) if isinstance(payload.get("waivers", []), list) else [] + if add_args is not None: + raw_waivers = add_waiver(raw_waivers, add_args, today) + waivers, failures, warnings = validate_waivers(raw_waivers, today) + active = [item for item in waivers if item["status"] == "active"] + expired = [item for item in waivers if item["status"] == "expired"] + invalid = [item for item in waivers if item["status"] == "invalid"] + covered_gate_keys = sorted({item["gate_key"] for item in active}) + waiver_candidates = build_waiver_candidates(skill_dir, covered_gate_keys, today) + waiverable_open = [ + item for item in waiver_candidates if item["waiver_allowed"] and item["status"] != "covered" + ] + non_waivable = [item for item in waiver_candidates if not item["waiver_allowed"]] + report = { + "schema_version": "1.0", + "ok": not failures, + "skill_dir": display_path(skill_dir), + "generated_at": generated_at or today.isoformat(), + "summary": { + "waiver_count": len(waivers), + "active_count": len(active), + "expired_count": len(expired), + "invalid_count": len(invalid), + "covered_gate_count": len(covered_gate_keys), + "covered_gate_keys": covered_gate_keys, + "waiver_candidate_count": len(waiver_candidates), + "waiverable_open_count": len(waiverable_open), + "non_waivable_count": len(non_waivable), + }, + "policy": { + "blocker_waivers_allowed": False, + "minimum_reason_chars": MIN_REASON_CHARS, + "expires_required": True, + "review_studio_gate_keys": sorted(REVIEW_STUDIO_GATE_KEYS), + "known_gate_keys": sorted(KNOWN_GATE_KEYS), + "waiverable_gate_keys": sorted(WAIVERABLE_GATE_KEYS), + "non_waivable_gate_keys": sorted(NON_WAIVABLE_GATE_KEYS), + }, + "waivers": waivers, + "waiver_candidates": waiver_candidates, + "failures": failures, + "warnings": warnings, + "artifacts": { + "json": display_path(output_json), + "markdown": display_path(output_md), + }, + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Review Waivers", + "", + f"- OK: `{report['ok']}`", + f"- Waivers: `{summary['waiver_count']}`", + f"- Active: `{summary['active_count']}`", + f"- Expired: `{summary['expired_count']}`", + f"- Invalid: `{summary['invalid_count']}`", + f"- Covered gates: `{', '.join(summary['covered_gate_keys']) or 'none'}`", + f"- Waiver candidates: `{summary['waiver_candidate_count']}`", + f"- Open waiverable candidates: `{summary['waiverable_open_count']}`", + f"- Non-waivable boundaries: `{summary['non_waivable_count']}`", + "", + "## Policy", + "", + "- Blocker waivers allowed: `False`", + f"- Minimum reason chars: `{report['policy']['minimum_reason_chars']}`", + "- Expiry is required for every waiver.", + "- World-class evidence completion cannot be waived; it can only be proven by accepted ledger evidence.", + f"- Review Studio gates: `{', '.join(report['policy']['review_studio_gate_keys'])}`", + f"- Waiverable gates: `{', '.join(report['policy']['waiverable_gate_keys'])}`", + f"- Non-waivable gates: `{', '.join(report['policy']['non_waivable_gate_keys'])}`", + "", + "## Waivers", + "", + ] + if not report["waivers"]: + lines.append("- None") + else: + lines.extend(["| ID | Gate | Decision | Reviewer | Status | Expires | Reason |", "| --- | --- | --- | --- | --- | --- | --- |"]) + for item in report["waivers"]: + reason = str(item["reason"]).replace("|", "\\|") + lines.append( + f"| `{item['id']}` | `{item['gate_key']}` | `{item['decision']}` | {item['reviewer']} | `{item['status']}` | `{item['expires_at']}` | {reason} |" + ) + lines.extend(["", "## Candidate Actions", ""]) + candidates = report.get("waiver_candidates", []) + if not candidates: + lines.append("- None") + else: + lines.extend(["| Gate | Status | Waiver | Risk | Evidence |", "| --- | --- | --- | --- | --- |"]) + for item in candidates: + risk = str(item.get("risk_summary", "")).replace("|", "\\|") + lines.append( + f"| `{item['gate_key']}` | `{item['status']}` | `{str(item['waiver_allowed']).lower()}` | {risk} | `{item.get('suggested_evidence', '')}` |" + ) + for item in candidates: + lines.extend(["", f"### {item['label']}", ""]) + lines.append(f"- gate: `{item['gate_key']}`") + lines.append(f"- status: `{item['status']}`") + lines.append(f"- waiver allowed: `{str(item['waiver_allowed']).lower()}`") + lines.append(f"- risk: {item['risk_summary']}") + lines.append(f"- evidence: `{item.get('suggested_evidence', '')}`") + lines.append(f"- verification: `{item.get('suggested_command', '')}`") + lines.append(f"- world-class boundary: {item.get('world_class_boundary', '')}") + lines.extend(["", "#### Required Review", ""]) + lines.extend(f"- {review}" for review in item.get("required_review", [])) + lines.extend(["", "## Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) + lines.extend(["", "## Warnings", ""]) + lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"]) + return "\n".join(lines).strip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render or update Review Studio waiver evidence.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--waivers-json") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--generated-at") + parser.add_argument("--add-waiver", action="store_true") + parser.add_argument("--gate-key", choices=sorted(KNOWN_GATE_KEYS)) + parser.add_argument("--decision", choices=sorted(VALID_DECISIONS), default="accepted-risk") + parser.add_argument("--reviewer") + parser.add_argument("--reason") + parser.add_argument("--expires-at") + parser.add_argument("--created-at") + parser.add_argument("--evidence") + parser.add_argument("--scope", default="current-release") + args = parser.parse_args() + + add_args = None + if args.add_waiver: + required = { + "--gate-key": args.gate_key, + "--reviewer": args.reviewer, + "--reason": args.reason, + "--expires-at": args.expires_at, + } + missing = [name for name, value in required.items() if not value] + if missing: + print(json.dumps({"ok": False, "failures": [f"Missing required fields for --add-waiver: {', '.join(missing)}"]}, ensure_ascii=False, indent=2)) + raise SystemExit(2) + add_args = args + + payload = render_report( + Path(args.skill_dir), + waivers_json=Path(args.waivers_json).resolve() if args.waivers_json else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + output_md=Path(args.output_md).resolve() if args.output_md else None, + generated_at=args.generated_at, + add_args=add_args, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_skill_interpretation.py b/scripts/render_skill_interpretation.py new file mode 100644 index 0000000..5c8cf35 --- /dev/null +++ b/scripts/render_skill_interpretation.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from render_skill_overview import render_html +from skill_report_model import build_report_model + + +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders the first-class skill interpretation report while reusing the Skill Overview v2 model and layout." + + +def build_interpretation_model(skill_dir: Path) -> dict: + summary = build_report_model(skill_dir) + contract = dict(summary.get("report_contract", {})) + contract.update( + { + "schema_version": "2.0", + "report_kind": "skill-interpretation", + "canonical_overview_report": "reports/skill-overview.html", + "html_report": "reports/skill-interpretation.html", + "json_report": "reports/skill-interpretation.json", + "layout": "kami-white-audit-v2", + "default_language": "zh-CN", + "languages": ["zh-CN", "en"], + "purpose": "Explain the generated skill's role, principles, usage scenarios, trigger contract, inputs, outputs, quality evidence, risks, assets, highlights, and next upgrade directions.", + } + ) + summary["report_contract"] = contract + summary["interpretation_contract"] = { + "schema_version": "2.0", + "source_model": "skill-overview-v2", + "source_model_reused": True, + "overview_report": "reports/skill-overview.html", + "html_report": "reports/skill-interpretation.html", + "json_report": "reports/skill-interpretation.json", + "default_language": "zh-CN", + "languages": ["zh-CN", "en"], + "includes": [ + "skill role", + "principles", + "usage scenarios", + "trigger contract", + "inputs and outputs", + "quality evidence", + "risk governance", + "package assets", + "highlights", + "upgrade roadmap", + ], + } + deliverables = summary.get("skill_summary", {}).get("deliverables", []) + for artifact in ["reports/skill-interpretation.html", "reports/skill-interpretation.json"]: + if artifact not in deliverables: + deliverables.append(artifact) + if "skill_summary" in summary: + summary["skill_summary"]["deliverables"] = deliverables + return summary + + +def render_skill_interpretation(skill_dir: Path, output_html: Path | None = None, output_json: Path | None = None) -> dict: + 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 / "skill-interpretation.html" + output_json = output_json or reports_dir / "skill-interpretation.json" + + summary = build_interpretation_model(skill_dir) + output_html.write_text(render_html(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8") + + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "html": str(output_html), + "json": str(output_json), + "overview_html": str(skill_dir / "reports" / "skill-overview.html"), + }, + "summary": { + "name": summary.get("name"), + "report_kind": summary.get("report_contract", {}).get("report_kind"), + "default_language": summary.get("report_contract", {}).get("default_language"), + "section_count": len(summary.get("report_contract", {}).get("nav_labels", [])), + "source_model_reused": summary.get("interpretation_contract", {}).get("source_model_reused"), + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render the first-class HTML skill interpretation report 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() + + result = render_skill_interpretation( + Path(args.skill_dir), + Path(args.output_html).resolve() if args.output_html else None, + Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_skill_os2_audit.py b/scripts/render_skill_os2_audit.py new file mode 100644 index 0000000..eba7035 --- /dev/null +++ b/scripts/render_skill_os2_audit.py @@ -0,0 +1,433 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +STATUS_LABELS = { + "pass": "pass", + "warn": "warn", + "human_required": "human-required", + "external_required": "external-required", + "missing": "missing", +} + + +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 rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def evidence(skill_dir: Path, *paths: str) -> list[dict[str, Any]]: + items = [] + for rel in paths: + path = skill_dir / rel + items.append({"path": rel, "exists": path.exists()}) + return items + + +def status_from(condition: bool, fallback: str = "missing") -> str: + return "pass" if condition else fallback + + +def audit_item( + key: str, + label: str, + status: str, + current: str, + target: str, + evidence_items: list[dict[str, Any]], + next_action: str, +) -> dict[str, Any]: + return { + "key": key, + "label": label, + "status": status, + "current": current, + "target": target, + "evidence": evidence_items, + "next_action": next_action, + } + + +def count_sources(events_summary: dict[str, Any]) -> dict[str, int]: + values = events_summary.get("source_types", {}) + return values if isinstance(values, dict) else {} + + +def build_audit(skill_dir: Path, generated_at: str) -> dict[str, Any]: + reports = skill_dir / "reports" + skill_ir = load_json(skill_dir / "skill-ir" / "examples" / "yao-meta-skill.json") + compiled = load_json(reports / "compiled_targets.json") + output_quality = load_json(reports / "output_quality_scorecard.json") + output_execution = load_json(reports / "output_execution_runs.json") + output_review = load_json(reports / "output_review_adjudication.json") + benchmark_reproducibility = load_json(reports / "benchmark_reproducibility.json") + conformance = load_json(reports / "conformance_matrix.json") + trust = load_json(reports / "security_trust_report.json") + runtime_permissions = load_json(reports / "runtime_permission_probes.json") + atlas = load_json(reports / "skill_atlas.json") + registry = load_json(reports / "registry_audit.json") + package_verification = load_json(reports / "package_verification.json") + install = load_json(reports / "install_simulation.json") + review_studio = load_json(reports / "review-studio.json") + adoption = load_json(reports / "adoption_drift_report.json") + telemetry_hooks = load_json(reports / "telemetry_hook_recipes.json") + + compiled_summary = compiled.get("summary", {}) + output_summary = output_quality.get("summary", {}) + execution_summary = output_execution.get("summary", {}) + review_summary = output_review.get("summary", {}) + benchmark_summary = benchmark_reproducibility.get("summary", {}) + conformance_summary = conformance.get("summary", {}) + trust_summary = trust.get("summary", {}) + permission_summary = runtime_permissions.get("summary", {}) + atlas_summary = atlas.get("summary", {}) + package_summary = package_verification.get("summary", {}) + install_summary = install.get("summary", {}) + studio_summary = review_studio.get("summary", {}) + adoption_summary = adoption.get("summary", {}) + telemetry_summary = telemetry_hooks.get("summary", {}) + sources = count_sources(adoption_summary) + + items = [ + audit_item( + "skill-ir", + "Skill IR", + status_from( + skill_ir.get("schema_version") == "2.0.0" + and bool((skill_dir / "skill-ir" / "schema.json").exists()) + and bool((skill_dir / "skill-ir" / "examples" / "yao-meta-skill.json").exists()) + ), + f"schema {skill_ir.get('schema_version', 'missing')}; targets {len(skill_ir.get('targets', []))}", + "2.0 schema, root export, and target-neutral contract evidence", + evidence(skill_dir, "skill-ir/schema.json", "skill-ir/examples/yao-meta-skill.json", "references/skill-ir-method.md"), + "Keep IR as the source before target packaging.", + ), + audit_item( + "target-compiler", + "Target Compiler", + status_from(compiled_summary.get("target_count", 0) >= 5 and compiled_summary.get("pass_count") == compiled_summary.get("target_count")), + f"{compiled_summary.get('pass_count', 0)}/{compiled_summary.get('target_count', 0)} targets pass", + "OpenAI, Claude, generic, Agent Skills compatible, and VS Code contracts generated from IR", + evidence(skill_dir, "scripts/compile_skill.py", "reports/compiled_targets.json", "tests/verify_compile_skill.py"), + "Deepen target-native transforms when provider clients expose stronger runtime APIs.", + ), + audit_item( + "output-eval-lab", + "Output Eval Lab", + status_from( + output_summary.get("case_count", 0) >= 5 + and output_summary.get("gate_pass") is True + and execution_summary.get("variant_run_count", 0) >= 10 + and output_summary.get("blind_pair_count", 0) >= 5 + ), + ( + f"{output_summary.get('case_count', 0)} cases; " + f"delta {output_summary.get('delta', 'n/a')}; " + f"exec {execution_summary.get('variant_run_count', 0)}; " + f"blind {output_summary.get('blind_pair_count', 0)}" + ), + "with-skill/baseline, assertions, execution evidence, blind A/B, failure taxonomy", + evidence( + skill_dir, + "evals/output/cases.jsonl", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "reports/output_quality_scorecard.json", + "reports/output_execution_runs.json", + "reports/output_blind_review_pack.json", + ), + "Add more real-file and adversarial holdout cases as usage grows.", + ), + audit_item( + "provider-holdout", + "Provider Holdout", + "pass" if execution_summary.get("model_executed_count", 0) > 0 else "external_required", + f"model-executed {execution_summary.get('model_executed_count', 0)}; token-observed {execution_summary.get('token_observed_count', 0)}", + "At least one real provider-backed holdout run with observed model/timing/token metadata", + evidence(skill_dir, "scripts/provider_output_eval_runner.py", "reports/output_execution_runs.json"), + "Run provider-backed holdout cases with real credentials and commit only aggregate evidence.", + ), + audit_item( + "human-adjudication", + "Human Adjudication", + "pass" if review_summary.get("ready_for_human_evidence") is True else "human_required", + f"{review_summary.get('judgment_count', 0)}/{review_summary.get('pair_count', 0)} decisions; pending {review_summary.get('pending_count', 0)}", + "Real reviewer decisions, blind-review attestation, and integrity fingerprints recorded before claiming output review completion", + evidence(skill_dir, "reports/output_review_decisions.json", "reports/output_review_adjudication.json", "scripts/adjudicate_output_review.py"), + "Record real A/B choices, reviewer metadata, and blind-review attestation, then regenerate adjudication.", + ), + audit_item( + "benchmark-reproducibility", + "Benchmark Reproducibility", + status_from( + benchmark_reproducibility.get("ok") is True + and benchmark_summary.get("reproducibility_ready") is True + and benchmark_summary.get("failure_disclosure_count", 0) > 0 + and benchmark_summary.get("missing_artifact_count", 1) == 0 + ), + ( + f"artifacts {benchmark_summary.get('required_artifact_count', 0)}; " + f"missing {benchmark_summary.get('missing_artifact_count', 'n/a')}; " + f"failures {benchmark_summary.get('failure_disclosure_count', 0)}" + ), + "Public methodology, reproducible commands, required artifacts, and failure disclosure are machine-checkable", + evidence( + skill_dir, + "reports/benchmark_methodology.md", + "reports/benchmark_reproducibility.json", + "reports/benchmark_reproducibility.md", + "evals/failure-cases.md", + "tests/verify_benchmark_reproducibility.py", + ), + "Keep the manifest current with every benchmark, package, and release evidence change.", + ), + audit_item( + "runtime-conformance", + "Runtime Conformance", + status_from(conformance_summary.get("target_count", 0) >= 5 and conformance_summary.get("pass_count") == conformance_summary.get("target_count")), + f"{conformance_summary.get('pass_count', 0)}/{conformance_summary.get('target_count', 0)} targets pass", + "Target package structure, metadata, relative paths, and degradation notes pass", + evidence(skill_dir, "runtime/conformance/schema.json", "scripts/run_conformance_suite.py", "reports/conformance_matrix.json"), + "Keep target conformance fixtures updated as platform contracts change.", + ), + audit_item( + "trust-security", + "Trust Security", + status_from( + trust.get("ok") is True + and trust_summary.get("secret_findings", 1) == 0 + and trust_summary.get("help_smoke_failed_count", 1) == 0 + and trust_summary.get("permission_missing_count", 1) == 0 + ), + ( + f"secrets {trust_summary.get('secret_findings', 'n/a')}; " + f"scripts {trust_summary.get('script_count', 'n/a')}; " + f"help failures {trust_summary.get('help_smoke_failed_count', 'n/a')}" + ), + "Secrets, scripts, dependencies, permissions, and package hash are reviewable", + evidence(skill_dir, "scripts/trust_check.py", "reports/security_trust_report.json", "security/permission_policy.json"), + "Keep high-permission approvals scoped, expiring, and target-mapped.", + ), + audit_item( + "runtime-permission-metadata", + "Permission Metadata", + status_from(permission_summary.get("target_count", 0) >= 4 and permission_summary.get("fail_count", 1) == 0), + ( + f"{permission_summary.get('pass_count', 0)}/{permission_summary.get('target_count', 0)} target probes pass; " + f"metadata fallback {permission_summary.get('metadata_fallback_count', 0)}; " + f"installer enforcement {permission_summary.get('installer_enforcement_pass_count', 0)}" + ), + "Packaged adapters expose explicit permission metadata, residual risks, and installer enforcement evidence when available", + evidence(skill_dir, "scripts/probe_runtime_permissions.py", "reports/runtime_permission_probes.json"), + "Preserve residual-risk notes until real native enforcement exists.", + ), + audit_item( + "native-permission-enforcement", + "Native Permission Enforcement", + "pass" if permission_summary.get("native_enforcement_count", 0) > 0 else "external_required", + ( + f"native-enforced targets {permission_summary.get('native_enforcement_count', 0)}; " + f"installer-enforced targets {permission_summary.get('installer_enforcement_pass_count', 0)}" + ), + "At least one target/client enforces approved permissions at runtime", + evidence(skill_dir, "reports/runtime_permission_probes.json", "reports/install_simulation.json", "security/permission_policy.json"), + "Integrate a real target-client or external installer runtime guard before claiming native permission enforcement.", + ), + audit_item( + "skill-atlas", + "Skill Atlas", + status_from( + atlas_summary.get("actionable_skill_count", 0) >= 1 + and atlas_summary.get("actionable_route_collision_count", 1) == 0 + and atlas_summary.get("actionable_owner_gap_count", 1) == 0 + ), + f"{atlas_summary.get('skill_count', 0)} skills; actionable collisions {atlas_summary.get('actionable_route_collision_count', 0)}", + "Workspace catalog, route overlap, stale/owner gaps, drift, and no-route opportunities", + evidence(skill_dir, "scripts/build_skill_atlas.py", "skill_atlas/catalog.json", "reports/skill_atlas.json", "skill_atlas/policy.json"), + "Feed real drift data into Atlas once client telemetry is installed.", + ), + audit_item( + "registry-distribution", + "Registry Distribution", + status_from( + registry.get("ok") is True + and package_verification.get("ok") is True + and install.get("ok") is True + and package_summary.get("archive_entry_count", 0) > 0 + and install_summary.get("installer_permission_failure_count", 1) == 0 + ), + ( + f"zip entries {package_summary.get('archive_entry_count', 0)}; " + f"install failures {install_summary.get('failure_count', 0)}; " + f"permission failures {install_summary.get('installer_permission_failure_count', 0)}" + ), + "Package metadata, archive checksum, package verification, and install simulation pass", + evidence(skill_dir, "registry/packages/yao-meta-skill.json", "reports/package_verification.json", "reports/install_simulation.json"), + "Regenerate registry after package verification so checksums stay aligned.", + ), + audit_item( + "review-studio", + "Review Studio", + status_from(studio_summary.get("gate_count", 0) >= 13 and studio_summary.get("blocker_count", 1) == 0), + f"decision {studio_summary.get('decision', 'missing')}; warnings {studio_summary.get('warning_count', 0)}; score {studio_summary.get('world_class_score', 'n/a')}", + "One page shows gates, evidence paths, blockers, warnings, actions, waivers, and annotations", + evidence(skill_dir, "scripts/render_review_studio.py", "reports/review-studio.json", "reports/review-studio.html"), + "Resolve human/external warning gates before claiming full release readiness.", + ), + audit_item( + "telemetry-drift", + "Telemetry Drift", + status_from( + adoption.get("ok") is True + and adoption.get("privacy_contract", {}).get("raw_content_allowed") is False + and telemetry_summary.get("recipe_count", 0) >= 5 + ), + f"events {adoption_summary.get('event_count', 0)}; risk {adoption_summary.get('risk_band', 'missing')}; recipes {telemetry_summary.get('recipe_count', 0)}", + "Local-first metadata-only event contract, aggregate drift report, hook recipes, and import path", + evidence(skill_dir, "reports/adoption_drift_report.json", "reports/telemetry_hook_recipes.json", "scripts/import_telemetry_events.py"), + "Keep raw JSONL out of distributed packages and use aggregate reports for Atlas.", + ), + audit_item( + "native-client-telemetry", + "Native Client Telemetry", + "pass" if sources.get("external", 0) > 0 and adoption_summary.get("adoption_sample_count", 0) > 0 else "external_required", + f"external source events {sources.get('external', 0)}; adoption samples {adoption_summary.get('adoption_sample_count', 0)}", + "A real Browser/Chrome/provider client sends production metadata events", + evidence(skill_dir, "scripts/telemetry_native_host.py", "reports/adoption_drift_report.json"), + "Install a real client against the native host and import production metadata-only events.", + ), + ] + + counts: dict[str, int] = {key: 0 for key in STATUS_LABELS} + for item in items: + counts[item["status"]] = counts.get(item["status"], 0) + 1 + open_gap_count = sum(counts.get(key, 0) for key in ("warn", "human_required", "external_required", "missing")) + local_foundation_ready = counts.get("missing", 0) == 0 and counts.get("warn", 0) == 0 + world_class_ready = open_gap_count == 0 + summary = { + "item_count": len(items), + "pass_count": counts.get("pass", 0), + "warn_count": counts.get("warn", 0), + "human_required_count": counts.get("human_required", 0), + "external_required_count": counts.get("external_required", 0), + "missing_count": counts.get("missing", 0), + "open_gap_count": open_gap_count, + "local_foundation_ready": local_foundation_ready, + "world_class_ready": world_class_ready, + "decision": "world-class-ready" if world_class_ready else "continue-iteration", + } + return { + "schema_version": "1.0", + "ok": counts.get("missing", 0) == 0, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": summary, + "status_counts": counts, + "items": items, + "next_highest_leverage": [ + item for item in items if item["status"] in {"human_required", "external_required", "warn", "missing"} + ][:5], + "artifacts": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "world_class_evidence_plan": "reports/world_class_evidence_plan.md", + }, + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Skill OS 2.0 Audit", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- pass: `{summary['pass_count']}` / `{summary['item_count']}`", + f"- human required: `{summary['human_required_count']}`", + f"- external required: `{summary['external_required_count']}`", + f"- missing: `{summary['missing_count']}`", + f"- world-class ready: `{str(summary['world_class_ready']).lower()}`", + "- evidence plan: `reports/world_class_evidence_plan.md`", + "", + "## Audit Items", + "", + "| Area | Status | Current | Target | Next action |", + "| --- | --- | --- | --- | --- |", + ] + for item in report["items"]: + lines.append( + "| " + + " | ".join( + [ + item["label"], + STATUS_LABELS.get(item["status"], item["status"]), + item["current"].replace("|", "\\|"), + item["target"].replace("|", "\\|"), + item["next_action"].replace("|", "\\|"), + ] + ) + + " |" + ) + lines.extend(["", "## Open Highest-Leverage Gaps", ""]) + for item in report["next_highest_leverage"]: + lines.append(f"- `{item['key']}` ({STATUS_LABELS.get(item['status'], item['status'])}): {item['next_action']}") + if not report["next_highest_leverage"]: + lines.append("- None.") + lines.extend(["", "## Evidence", ""]) + for item in report["items"]: + paths = [entry["path"] for entry in item["evidence"] if entry.get("exists")] + missing = [entry["path"] for entry in item["evidence"] if not entry.get("exists")] + lines.append(f"### {item['label']}") + lines.append("") + lines.append(f"- existing evidence: {', '.join(f'`{path}`' for path in paths) if paths else '`none`'}") + if missing: + lines.append(f"- missing evidence: {', '.join(f'`{path}`' for path in missing)}") + lines.append("") + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a requirement-by-requirement Skill OS 2.0 audit.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json", default="reports/skill_os2_audit.json") + parser.add_argument("--output-md", default="reports/skill_os2_audit.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = build_audit(skill_dir, args.generated_at) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_skill_os2_coverage.py b/scripts/render_skill_os2_coverage.py new file mode 100644 index 0000000..aee29bd --- /dev/null +++ b/scripts/render_skill_os2_coverage.py @@ -0,0 +1,555 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + +from skill_os2_coverage_markdown import render_markdown + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders a Skill OS 2.0 blueprint-to-evidence coverage audit." +RECOMMENDED_PR_LABELS = { + "benchmark-methodology": "Benchmark Methodology", + "output-eval-schema": "Output Eval Schema", + "output-eval-runner": "Output Eval Runner", + "output-quality-scorecard": "Output Quality Scorecard", + "skill-ir-v0": "Skill IR V0", + "compiler-refactor": "Compiler Refactor", + "agent-skills-conformance": "Agent Skills Conformance", + "trust-check": "Trust Check", + "skill-atlas-generator": "Skill Atlas Generator", + "registry-package-format": "Registry Package Format", + "review-studio-2": "Review Studio 2.0", + "migration-v2-docs": "Migration V2 Docs", + "evidence-consistency": "Evidence Consistency", +} + + +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 rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def evidence(skill_dir: Path, paths: list[str]) -> list[dict[str, Any]]: + return [{"path": item, "exists": (skill_dir / item).exists()} for item in paths] + + +def all_exist(items: list[dict[str, Any]]) -> bool: + return all(item["exists"] for item in items) + + +def paths_exist(skill_dir: Path, paths: list[str]) -> bool: + return all((skill_dir / item).exists() for item in paths) + + +def latest_daily_skillops_paths(skill_dir: Path) -> list[str]: + base_paths = [ + "scripts/render_daily_skillops_report.py", + "tests/verify_daily_skillops.py", + ] + daily_dir = skill_dir / "reports" / "skillops" / "daily" + daily_json_reports = sorted(daily_dir.glob("*.json")) if daily_dir.exists() else [] + if not daily_json_reports: + return [ + *base_paths, + "reports/skillops/daily/YYYY-MM-DD.json", + "reports/skillops/daily/YYYY-MM-DD.md", + ] + latest_json = daily_json_reports[-1] + latest_md = latest_json.with_suffix(".md") + return [*base_paths, rel_path(latest_json, skill_dir), rel_path(latest_md, skill_dir)] + + +def latest_weekly_curator_paths(skill_dir: Path) -> list[str]: + base_paths = [ + "scripts/render_weekly_curator_report.py", + "tests/verify_weekly_curator.py", + ] + weekly_dir = skill_dir / "reports" / "skillops" / "weekly" + weekly_json_reports = sorted(weekly_dir.glob("*.json")) if weekly_dir.exists() else [] + if not weekly_json_reports: + return [ + *base_paths, + "reports/skillops/weekly/YYYY-WNN.json", + "reports/skillops/weekly/YYYY-WNN.md", + ] + latest_json = weekly_json_reports[-1] + latest_md = latest_json.with_suffix(".md") + return [*base_paths, rel_path(latest_json, skill_dir), rel_path(latest_md, skill_dir)] + + +def condition_status(condition: bool, evidence_items: list[dict[str, Any]]) -> str: + if not all_exist(evidence_items): + return "missing" + return "pass" if condition else "warn" + + +def build_item( + *, + key: str, + category: str, + label: str, + objective: str, + artifact_paths: list[str], + command: str, + test: str, + current: str, + condition: bool, + next_action: str, + skill_dir: Path, +) -> dict[str, Any]: + evidence_items = evidence(skill_dir, artifact_paths) + status = condition_status(condition, evidence_items) + return { + "key": key, + "category": category, + "label": label, + "status": status, + "objective": objective, + "current": current, + "command": command, + "test": test, + "evidence": evidence_items, + "next_action": next_action, + } + + +def build_extension_track( + *, + key: str, + label: str, + status: str, + objective: str, + current: str, + target: str, + artifact_paths: list[str], + next_action: str, + skill_dir: Path, +) -> dict[str, Any]: + return { + "key": key, + "label": label, + "status": status, + "objective": objective, + "current": current, + "target": target, + "evidence": evidence(skill_dir, artifact_paths), + "next_action": next_action, + } + + +def summary_value(payload: dict[str, Any], key: str, default: Any = 0) -> Any: + summary = payload.get("summary", {}) + return summary.get(key, default) if isinstance(summary, dict) else default + + +def target_names(payload: dict[str, Any]) -> set[str]: + names = set() + for item in payload.get("targets", []) or payload.get("results", []): + if isinstance(item, dict): + value = item.get("target") or item.get("platform") or item.get("name") + if value: + names.add(str(value)) + return names + + +def build_coverage(skill_dir: Path, generated_at: str) -> dict[str, Any]: + reports = skill_dir / "reports" + skill_ir = load_json(skill_dir / "skill-ir" / "examples" / "yao-meta-skill.json") + output_quality = load_json(reports / "output_quality_scorecard.json") + output_execution = load_json(reports / "output_execution_runs.json") + compiled = load_json(reports / "compiled_targets.json") + conformance = load_json(reports / "conformance_matrix.json") + trust = load_json(reports / "security_trust_report.json") + atlas = load_json(reports / "skill_atlas.json") + registry = load_json(reports / "registry_audit.json") + package_verification = load_json(reports / "package_verification.json") + install = load_json(reports / "install_simulation.json") + review_studio = load_json(reports / "review-studio.json") + adoption = load_json(reports / "adoption_drift_report.json") + telemetry_hooks = load_json(reports / "telemetry_hook_recipes.json") + benchmark = load_json(reports / "benchmark_reproducibility.json") + world_class_ledger = load_json(reports / "world_class_evidence_ledger.json") + evidence_consistency = load_json(reports / "evidence_consistency.json") + + output_case_count = summary_value(output_quality, "case_count") + output_delta = summary_value(output_quality, "delta", "n/a") + execution_count = summary_value(output_execution, "variant_run_count") + compiled_count = summary_value(compiled, "target_count") + compiled_pass = summary_value(compiled, "pass_count") + conformance_count = summary_value(conformance, "target_count") + conformance_pass = summary_value(conformance, "pass_count") + script_count = summary_value(trust, "script_count") + atlas_skill_count = summary_value(atlas, "skill_count") + package_entries = summary_value(package_verification, "archive_entry_count") + install_failures = summary_value(install, "failure_count") + studio_gates = summary_value(review_studio, "gate_count") + telemetry_recipe_count = summary_value(telemetry_hooks, "recipe_count") + benchmark_artifacts = summary_value(benchmark, "required_artifact_count") + pending_world_class = summary_value(world_class_ledger, "pending_count") + + modules = [ + build_item( + key="skill-ir", + category="core-module", + label="Skill IR", + objective="Platform-neutral capability contract exists before platform-specific packaging.", + artifact_paths=["skill-ir/schema.json", "skill-ir/examples/yao-meta-skill.json", "scripts/export_skill_ir.py", "tests/verify_skill_ir.py"], + command="python3 scripts/yao.py skill-ir .", + test="python3 tests/verify_skill_ir.py", + current=f"schema {skill_ir.get('schema_version', 'missing')}; targets {len(skill_ir.get('targets', []))}", + condition=skill_ir.get("schema_version") == "2.0.0" and len(skill_ir.get("targets", [])) >= 5, + next_action="Keep all target packages compiled from IR rather than hand-maintained per target.", + skill_dir=skill_dir, + ), + build_item( + key="output-eval-lab", + category="core-module", + label="Output Eval Lab", + objective="with-skill/baseline output quality is measured with assertions, execution evidence, and blind review packs.", + artifact_paths=["evals/output/schema.json", "evals/output/cases.jsonl", "scripts/run_output_eval.py", "scripts/run_output_execution.py", "reports/output_quality_scorecard.json", "tests/verify_output_eval_lab.py"], + command="python3 scripts/yao.py output-exec . && python3 scripts/yao.py output-review .", + test="python3 tests/verify_output_eval_lab.py", + current=f"{output_case_count} cases; delta {output_delta}; execution {execution_count}", + condition=output_case_count >= 5 and execution_count >= 10 and summary_value(output_quality, "gate_pass") is True, + next_action="Add more real-file and adversarial holdout cases as adoption data grows.", + skill_dir=skill_dir, + ), + build_item( + key="runtime-conformance", + category="core-module", + label="Runtime Conformance", + objective="Target packages can be consumed by OpenAI, Claude, Agent Skills, VS Code, and generic targets.", + artifact_paths=["runtime/conformance/schema.json", "scripts/run_conformance_suite.py", "reports/conformance_matrix.json", "tests/verify_conformance_suite.py"], + command="python3 scripts/yao.py conformance .", + test="python3 tests/verify_conformance_suite.py", + current=f"{conformance_pass}/{conformance_count} targets pass", + condition=conformance_count >= 5 and conformance_pass == conformance_count and "agent-skills" in target_names(conformance), + next_action="Keep conformance cases aligned with current platform metadata rules.", + skill_dir=skill_dir, + ), + build_item( + key="trust-security", + category="core-module", + label="Trust Security", + objective="Scripts, dependencies, permissions, secrets, and package hash are reviewable for team distribution.", + artifact_paths=["scripts/trust_check.py", "security/trust_policy.md", "security/script_policy.md", "security/permission_policy.json", "reports/security_trust_report.json", "tests/verify_trust_check.py"], + command="python3 scripts/yao.py trust .", + test="python3 tests/verify_trust_check.py", + current=f"{script_count} scripts; secrets {summary_value(trust, 'secret_findings', 'n/a')}; help failures {summary_value(trust, 'help_smoke_failed_count', 'n/a')}", + condition=trust.get("ok") is True and summary_value(trust, "secret_findings", 1) == 0 and summary_value(trust, "help_smoke_failed_count", 1) == 0, + next_action="Keep high-permission approvals scoped, expiring, and mapped to target enforcement.", + skill_dir=skill_dir, + ), + build_item( + key="skill-atlas", + category="core-module", + label="Skill Atlas", + objective="Team skill portfolio reveals route collisions, stale ownership, dependency graph, and no-route opportunities.", + artifact_paths=["scripts/build_skill_atlas.py", "skill_atlas/catalog.json", "skill_atlas/route_overlap_matrix.csv", "skill_atlas/dependency_graph.json", "reports/skill_atlas.json", "tests/verify_skill_atlas.py"], + command="python3 scripts/yao.py skill-atlas --workspace-root .", + test="python3 tests/verify_skill_atlas.py", + current=f"{atlas_skill_count} scanned skills; actionable collisions {summary_value(atlas, 'actionable_route_collision_count')}", + condition=atlas_skill_count >= 1 and summary_value(atlas, "actionable_route_collision_count", 1) == 0, + next_action="Use real telemetry to rank stale or drifting skills by impact, not only by static metadata.", + skill_dir=skill_dir, + ), + build_item( + key="registry-distribution", + category="core-module", + label="Registry Distribution", + objective="Skill packages are installable, versioned, checksumed, and upgrade-reviewable.", + artifact_paths=["registry/index.schema.json", "registry/package.schema.json", "registry/packages/yao-meta-skill.json", "scripts/registry_audit.py", "reports/package_verification.json", "reports/install_simulation.json", "tests/verify_registry_audit.py"], + command="python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip && python3 scripts/yao.py registry-audit .", + test="python3 tests/verify_registry_audit.py", + current=f"archive entries {package_entries}; install failures {install_failures}", + condition=registry.get("ok") is True and package_verification.get("ok") is True and install.get("ok") is True and package_entries > 0 and install_failures == 0, + next_action="Regenerate registry metadata after package verification so source and archive checksums stay aligned.", + skill_dir=skill_dir, + ), + build_item( + key="review-studio", + category="core-module", + label="Review Studio", + objective="One HTML page supports first-pass production review across trigger, output, runtime, trust, release, and evidence actions.", + artifact_paths=["scripts/render_review_studio.py", "reports/review-studio.html", "reports/review-studio.json", "tests/verify_review_studio.py"], + command="python3 scripts/yao.py review-studio .", + test="python3 tests/verify_review_studio.py", + current=f"{studio_gates} gates; decision {summary_value(review_studio, 'decision', 'missing')}; warnings {summary_value(review_studio, 'warning_count')}", + condition=review_studio.get("ok") is True and studio_gates >= 14 and summary_value(review_studio, "blocker_count", 1) == 0, + next_action="Close pending human and external evidence before claiming full release readiness.", + skill_dir=skill_dir, + ), + build_item( + key="telemetry-drift", + category="core-module", + label="Telemetry Drift", + objective="Real usage feedback is captured as metadata-only local-first drift signals.", + artifact_paths=["scripts/emit_telemetry_event.py", "scripts/import_telemetry_events.py", "scripts/telemetry_native_host.py", "reports/adoption_drift_report.json", "reports/telemetry_hook_recipes.json", "tests/verify_telemetry_hooks.py"], + command="python3 scripts/yao.py telemetry-hooks . && python3 scripts/yao.py adoption-drift .", + test="python3 tests/verify_telemetry_hooks.py", + current=f"events {summary_value(adoption, 'event_count')}; recipes {telemetry_recipe_count}; risk {summary_value(adoption, 'risk_band', 'missing')}", + condition=adoption.get("ok") is True and adoption.get("privacy_contract", {}).get("raw_content_allowed") is False and telemetry_recipe_count >= 5, + next_action="Install a real client and import production metadata-only events into the local drift loop.", + skill_dir=skill_dir, + ), + ] + + recommended_prs = [ + ("benchmark-methodology", "reports/benchmark_methodology.md", "reports/benchmark_reproducibility.json", "tests/verify_benchmark_reproducibility.py", benchmark.get("ok") is True and summary_value(benchmark, "methodology_complete") is True, f"{benchmark_artifacts} required artifacts checked"), + ("output-eval-schema", "evals/output/schema.json", "evals/output/cases.jsonl", "tests/verify_output_eval_lab.py", output_case_count >= 5, f"{output_case_count} output cases"), + ("output-eval-runner", "scripts/run_output_eval.py", "reports/output_quality_scorecard.json", "tests/verify_output_eval_lab.py", output_quality.get("ok") is True, f"delta {output_delta}"), + ("output-quality-scorecard", "reports/output_quality_scorecard.md", "reports/output_quality_scorecard.json", "tests/verify_output_eval_lab.py", summary_value(output_quality, "gate_pass") is True, f"gate pass {summary_value(output_quality, 'gate_pass', False)}"), + ("skill-ir-v0", "skill-ir/schema.json", "skill-ir/examples/yao-meta-skill.json", "tests/verify_skill_ir.py", skill_ir.get("schema_version") == "2.0.0", f"schema {skill_ir.get('schema_version', 'missing')}"), + ("compiler-refactor", "scripts/compile_skill.py", "reports/compiled_targets.json", "tests/verify_compile_skill.py", compiled_count >= 5 and compiled_pass == compiled_count, f"{compiled_pass}/{compiled_count} compiled targets"), + ("agent-skills-conformance", "runtime/conformance/schema.json", "reports/conformance_matrix.json", "tests/verify_conformance_suite.py", "agent-skills" in target_names(conformance), "agent-skills target present"), + ("trust-check", "scripts/trust_check.py", "reports/security_trust_report.json", "tests/verify_trust_check.py", trust.get("ok") is True, f"secret findings {summary_value(trust, 'secret_findings', 'n/a')}"), + ("skill-atlas-generator", "scripts/build_skill_atlas.py", "reports/skill_atlas.json", "tests/verify_skill_atlas.py", atlas.get("ok") is True, f"{atlas_skill_count} scanned skills"), + ("registry-package-format", "registry/package.schema.json", "reports/registry_audit.json", "tests/verify_registry_audit.py", registry.get("ok") is True, f"registry ok {registry.get('ok') is True}"), + ("review-studio-2", "scripts/render_review_studio.py", "reports/review-studio.html", "tests/verify_review_studio.py", review_studio.get("ok") is True and studio_gates >= 14, f"{studio_gates} review gates"), + ("migration-v2-docs", "docs/migration-v2.md", "reports/skill-os-2-review.md", "README.md", (skill_dir / "docs" / "migration-v2.md").exists(), "migration guide present"), + ("evidence-consistency", "scripts/render_evidence_consistency.py", "reports/evidence_consistency.json", "tests/verify_evidence_consistency.py", evidence_consistency.get("ok") is True and summary_value(evidence_consistency, "fail_count") == 0, f"{summary_value(evidence_consistency, 'check_count')} consistency checks"), + ] + pr_items = [ + build_item( + key=key, + category="recommended-pr", + label=RECOMMENDED_PR_LABELS[key], + objective="Recommended Skill OS 2.0 implementation PR from the upgrade plan.", + artifact_paths=[path_a, path_b, path_c], + command="make ci-test", + test=path_c if path_c.startswith("tests/") else "docs review", + current=current, + condition=condition, + next_action="Keep this item covered as the implementation evolves.", + skill_dir=skill_dir, + ) + for key, path_a, path_b, path_c, condition, current in recommended_prs + ] + + interpretation_paths = [ + "reports/skill-overview.html", + "reports/skill-overview.json", + "scripts/render_skill_overview.py", + "scripts/render_skill_interpretation.py", + "schemas/skill-interpretation.schema.json", + "tests/verify_skill_interpretation.py", + ] + interpretation_ready = paths_exist(skill_dir, interpretation_paths) + interpretation_status = "covered" if interpretation_ready else "partial" + interpretation_current = ( + "Skill Overview v2 is canonical and mirrored as first-class skill-interpretation HTML/JSON with schema and tests." + if interpretation_ready + else "Skill Overview v2 already covers much of the explainer experience, but dedicated skill-interpretation JSON/HTML artifacts are not first-class yet." + ) + interpretation_next_action = ( + "Keep overview and interpretation contracts in lockstep when report sections, metrics, or layout semantics change." + if interpretation_ready + else "Decide whether overview v2 is the canonical interpretation surface; if not, add a dedicated schema, renderer, and CJK/path-safety tests." + ) + extension_tracks = [ + build_extension_track( + key="skill-interpretation-report", + label="Skill Interpretation Report", + status=interpretation_status, + objective="User-facing deep interpretation report explains use cases, triggers, inputs, outputs, workflow, principles, boundaries, quality gates, examples, and next iterations.", + current=interpretation_current, + target="Either keep skill-overview as the canonical interpretation report with an explicit contract, or split a dedicated reports/skill-interpretation.* renderer and tests.", + artifact_paths=interpretation_paths, + next_action=interpretation_next_action, + skill_dir=skill_dir, + ), + ] + adaptive_foundation_paths = [ + "references/autonomous-adaptation.md", + "references/user-memory-policy.md", + "schemas/adaptation-proposal.schema.json", + "scripts/summarize_user_signals.py", + "scripts/propose_adaptation.py", + "tests/verify_adaptation_safety.py", + ] + adaptive_apply_paths = [ + "scripts/apply_adaptation.py", + "reports/adaptation_approval_ledger.json", + "reports/adaptation_regression_report.json", + ] + adaptive_foundation_ready = paths_exist(skill_dir, adaptive_foundation_paths) + adaptive_apply_ready = paths_exist(skill_dir, adaptive_apply_paths) + adaptive_status = "covered" if adaptive_foundation_ready and adaptive_apply_ready else ("partial" if adaptive_foundation_ready else "planned") + adaptive_current = ( + "Proposal-only adapt-scan/adapt-propose foundation exists with policy, schema, and safety tests; approval-gated patch application is not implemented yet." + if adaptive_status == "partial" + else ( + "Full adaptive loop includes proposal, approval, patch application, regression evidence, and rollback metadata." + if adaptive_status == "covered" + else "The repo has feedback, iteration, telemetry, and review artifacts, but no adapt-scan/adapt-propose/adapt-apply approval loop and no user-memory policy." + ) + ) + adaptive_next_action = ( + "Add adapt-apply only after approval ledger, allowlisted targets, dry-run diffs, regression reports, and rollback artifacts are designed." + if adaptive_status == "partial" + else "Start with policy and read-only scan tests; do not read shell history or private logs unless the user provides an explicit source path." + ) + extension_tracks.append( + build_extension_track( + key="adaptive-self-iteration", + label="Adaptive Self-Iteration", + status=adaptive_status, + objective="Local-first preference memory, repeated-signal extraction, adaptation proposals, approval, patch application, regression evidence, and rollback.", + current=adaptive_current, + target="Proposal-only adaptation with explicit input source, redaction, allowlisted write targets, approval ledger, regression report, and rollback plan.", + artifact_paths=[ + *adaptive_foundation_paths, + *adaptive_apply_paths, + "reports/user_patterns.json", + "reports/adaptation_proposals.json", + "reports/iteration-directions.md", + "reports/adoption_drift_report.md", + ], + next_action=adaptive_next_action, + skill_dir=skill_dir, + ) + ) + daily_skillops_paths = latest_daily_skillops_paths(skill_dir) + daily_skillops_ready = paths_exist(skill_dir, daily_skillops_paths) + daily_skillops_status = "covered" if daily_skillops_ready else "partial" + extension_tracks.append( + build_extension_track( + key="daily-skillops-report", + label="Daily SkillOps Report", + status=daily_skillops_status, + objective="Daily operations layer summarizes explicit-source conversation patterns, proposal-only adaptation work, approval state, release locks, and world-class evidence gaps.", + current=( + "Daily SkillOps report is a CLI-backed, explicit-source operations cockpit with tests and committed dated evidence." + if daily_skillops_ready + else "Adaptive scan/propose exists, but the daily operations report is not fully wired into scripts, tests, and evidence artifacts." + ), + target="Generate reports/skillops/daily/YYYY-MM-DD.* from explicit sources without private log scanning, source writes, auto-patching, or world-class overclaiming.", + artifact_paths=daily_skillops_paths, + next_action="Keep Daily SkillOps report aligned with proposal, approval, coverage, and world-class ledger contracts as the operations layer evolves.", + skill_dir=skill_dir, + ) + ) + weekly_curator_paths = latest_weekly_curator_paths(skill_dir) + weekly_curator_ready = paths_exist(skill_dir, weekly_curator_paths) + weekly_curator_status = "covered" if weekly_curator_ready else "partial" + extension_tracks.append( + build_extension_track( + key="weekly-curator-report", + label="Weekly Curator Report", + status=weekly_curator_status, + objective="Weekly curator layer aggregates Daily SkillOps opportunities, Skill Atlas portfolio signals, release locks, and world-class evidence gaps into a maintenance queue.", + current=( + "Weekly curator report is CLI-backed, proposal-only, and backed by committed weekly JSON/Markdown evidence." + if weekly_curator_ready + else "Daily SkillOps and Skill Atlas exist, but weekly curator aggregation is not fully wired into scripts, tests, and evidence artifacts." + ), + target="Generate reports/skillops/weekly/YYYY-WNN.* without private log scanning, source writes, auto-patching, or world-class overclaiming.", + artifact_paths=weekly_curator_paths, + next_action="Use weekly curator output as the Skill Librarian maintenance queue before approving any durable skill-library changes.", + skill_dir=skill_dir, + ) + ) + extension_counts: dict[str, int] = {} + for item in extension_tracks: + extension_counts[item["status"]] = extension_counts.get(item["status"], 0) + 1 + + items = modules + pr_items + counts: dict[str, int] = {"pass": 0, "warn": 0, "missing": 0} + for item in items: + counts[item["status"]] = counts.get(item["status"], 0) + 1 + local_ready = counts.get("missing", 0) == 0 and counts.get("warn", 0) == 0 + public_ready = local_ready and summary_value(world_class_ledger, "ready_to_claim_world_class", False) is True + decision = "world-class-ready" if public_ready else ("local-blueprint-covered-evidence-pending" if local_ready else "continue-implementation") + summary = { + "item_count": len(items), + "module_count": len(modules), + "recommended_pr_count": len(pr_items), + "pass_count": counts.get("pass", 0), + "warn_count": counts.get("warn", 0), + "missing_count": counts.get("missing", 0), + "extension_track_count": len(extension_tracks), + "extension_partial_count": extension_counts.get("partial", 0), + "extension_planned_count": extension_counts.get("planned", 0), + "extension_covered_count": extension_counts.get("covered", 0), + "adaptive_extension_ready": adaptive_status == "covered", + "local_blueprint_ready": local_ready, + "public_world_class_ready": public_ready, + "world_class_evidence_pending_count": pending_world_class, + "decision": decision, + } + return { + "schema_version": "1.0", + "ok": counts.get("missing", 0) == 0, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": summary, + "status_counts": counts, + "extension_status_counts": extension_counts, + "modules": modules, + "recommended_prs": pr_items, + "reference_extension_tracks": extension_tracks, + "next_highest_leverage": [ + "Close the four world-class evidence ledger entries with accepted human or external evidence.", + "Keep the first-class skill interpretation report and Skill Overview v2 contract synchronized as the report model evolves.", + "Use Daily SkillOps with explicit curated sources to review adaptation proposals without scanning private logs or auto-patching.", + "Keep the blueprint coverage report in CI so 2.0 plan drift is visible.", + ], + "source_blueprint": { + "title": "Skill Overview / Skill OS 2.0 upgrade plan", + "core_module_count": 8, + "recommended_pr_count": 13, + "reference_extension_count": len(extension_tracks), + "reference_extensions": [ + "Skill interpretation report", + "Adaptive self-iteration with local preference memory and approval gates", + "Daily SkillOps operations report", + ], + }, + "artifacts": { + "json": "reports/skill_os2_coverage.json", + "markdown": "reports/skill_os2_coverage.md", + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render Skill OS 2.0 blueprint coverage evidence.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json", default="reports/skill_os2_coverage.json") + parser.add_argument("--output-md", default="reports/skill_os2_coverage.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = build_coverage(skill_dir, args.generated_at) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_skill_overview.py b/scripts/render_skill_overview.py new file mode 100644 index 0000000..e14d7d0 --- /dev/null +++ b/scripts/render_skill_overview.py @@ -0,0 +1,588 @@ +#!/usr/bin/env python3 +import argparse +import html +import json +from pathlib import Path + +from skill_report_charts import render_chart_set +from skill_report_i18n import ( + KIND_ZH, + LABEL_EN, + PACKAGE_LABEL_ZH, + bi_item, + bi_span, + en_for, + mode_zh, + readable_description_zh, +) +from skill_report_layout import render_language_switch, render_report_nav, skill_overview_css, skill_overview_script +from skill_report_model import REPORT_NAV_V2, build_report_model + + +def render_list(items: list[str], class_name: str = "list") -> str: + if not items: + return f'
    • {bi_span("暂无记录。", "No records yet.")}
    ' + return f'
      ' + "".join(f"
    • {bi_item(str(item))}
    • " for item in items) + "
    " + + +def render_ordered_steps(items: list[str], class_name: str = "step-list") -> str: + if not items: + return f'
    1. {bi_span("暂无步骤。", "No steps yet.")}
    ' + return f'
      ' + "".join(f"
    1. {bi_item(str(item))}
    2. " for item in items) + "
    " + + +def render_metric_cards(scorecard: dict) -> str: + cards = [] + for key, item in scorecard.items(): + reasons = render_list(item.get("reasons", [])[:3], "compact-list") + cards.append( + "
    " + "
    " + f"{bi_item(str(item.get('label', key)))}" + f"{html.escape(str(item.get('score', 'n/a')))}" + "
    " + f"
    {reasons}
    " + "
    " + ) + return "".join(cards) + + +def render_metric_summary(scorecard: dict) -> str: + items = [] + for key, item in scorecard.items(): + score = int(item.get("score", 0)) + label = str(item.get("label", key)) + reason = str(item.get("reasons", [""])[0]) + if score >= 85: + verdict = "稳定" + verdict_en = "Stable" + elif score >= 70: + verdict = "可用" + verdict_en = "Usable" + else: + verdict = "关注" + verdict_en = "Watch" + items.append( + "
  • " + f"{bi_span(verdict, verdict_en)}" + f"{bi_item(label)}" + f"{score}" + f"{bi_item(reason)}" + "
  • " + ) + return "
      " + "".join(items) + "
    " + + +def render_audit_rows(items: list[dict]) -> str: + rows = [] + for item in items: + name = str(item.get("name", item.get("label", "项目"))) + response = str(item.get("response", item.get("kind", ""))) + rows.append( + "" + f"{bi_span(name, LABEL_EN.get(name, name))}" + f"{bi_item(str(item.get('signal', item.get('body', ''))))}" + f"{bi_span(response, LABEL_EN.get(response, response))}" + "" + ) + return "".join(rows) + + +WORLD_CLASS_CHECK_COPY = { + "Provider model run": ("提供商实跑", "Provider model run"), + "Token usage observed": ("Token 用量", "Token usage observed"), + "No pending decisions": ("无待判定", "No pending decisions"), + "Judgments complete": ("盲评完成", "Judgments complete"), + "Native enforcement": ("原生执行", "Native enforcement"), + "External events": ("外部事件", "External events"), + "Adoption sample": ("采用样本", "Adoption sample"), +} + + +def render_world_class_check(check: object) -> str: + if isinstance(check, dict): + label_en = str(check.get("label_en") or check.get("label") or "") + label_zh = str(check.get("label_zh") or "") + else: + label_en = str(check) + label_zh = "" + label_zh, label_en = WORLD_CLASS_CHECK_COPY.get(label_en, (label_zh or label_en, label_en)) + return f"
  • {bi_span(label_zh, label_en)}
  • " + + +def render_world_class_readiness(readiness: dict) -> str: + if not readiness: + return "" + kpis = [ + ( + "待补证据", + "Pending", + readiness.get("pending_count", 0), + "仍需外部或人工证据接受。", + "External or human evidence still needs acceptance.", + ), + ( + "已接受", + "Accepted", + readiness.get("accepted_count", 0), + "已通过 source check 与提交契约。", + "Passed source checks and submission contract.", + ), + ( + "源检查", + "Source Checks", + f"{readiness.get('source_pass_count', 0)} / {readiness.get('source_check_count', 0)}", + "通过数 / 总检查数。", + "Passed checks / total checks.", + ), + ] + kpi_html = "".join( + "
    " + f"{bi_span(zh, en)}" + f"{html.escape(str(value))}" + f"{bi_span(note_zh, note_en)}" + "
    " + for zh, en, value, note_zh, note_en in kpis + ) + entries = readiness.get("entries", []) + if entries: + blocks = [] + for item in entries: + blocked_checks = [ + str(check).strip() + for check in item.get("blocked_checks", []) + if str(check).strip() + ] + if blocked_checks: + checks_html = ( + "
      " + + "".join(render_world_class_check(check) for check in blocked_checks) + + "
    " + ) + else: + checks_html = ( + "

    " + f"{bi_span('当前没有阻塞检查。', 'No blocked checks right now.')}" + "

    " + ) + blocks.append( + "
    " + "
    " + f"{bi_span(str(item.get('category_zh', '外部证据')), str(item.get('category_en', 'External evidence')))}" + f"

    {bi_span(str(item.get('label_zh', item.get('key', '证据项'))), str(item.get('label_en', item.get('key', 'Evidence item'))))}

    " + "
    " + f"

    {bi_span(str(item.get('summary_zh', '仍待补充证据。')), str(item.get('summary_en', 'Evidence is still pending.')))}

    " + f"
    {bi_span('阻塞检查', 'Blocked Checks')}
    " + f"{checks_html}" + "
    " + ) + entry_html = "".join(blocks) + else: + entry_html = ( + "
    " + f"

    {bi_span('尚未生成 world-class ledger;这里只保留反过度承诺提示。', 'No world-class ledger has been generated; this panel keeps the anti-overclaim guard visible.')}

    " + "
    " + ) + status = "可宣称" if readiness.get("ready") else "证据待补" + status_en = "Claim-ready" if readiness.get("ready") else "Evidence pending" + return ( + "
    " + "
    " + "
    " + f"

    {bi_span('世界证据', 'World Evidence')}

    " + f"

    {bi_span(str(readiness.get('conclusion_zh', '世界级证据状态未知。')), str(readiness.get('conclusion_en', 'World-class evidence status is unknown.')))}

    " + "
    " + f"{bi_span(status, status_en)}" + "
    " + f"
    {kpi_html}
    " + f"
    {entry_html}
    " + "
    " + ) + + +def render_score_strip(scorecard: dict) -> str: + keys = ["completeness_score", "trigger_score", "evidence_score", "context_cost"] + cards = [] + for key in keys: + item = scorecard.get(key) + if not item: + continue + score = int(item.get("score", 0)) + reason = item.get("reasons", [""])[0] + cards.append( + "
    " + f"{bi_item(str(item.get('label', key)))}" + f"{score}" + f"" + f"{bi_item(str(reason))}" + "
    " + ) + return "".join(cards) + + +def render_roadmap(items: list[dict]) -> str: + blocks = [] + for index, item in enumerate(items[:3], start=1): + actions = render_list([str(action) for action in item.get("actions", [])], "compact-list") + blocks.append( + "
    " + f"{bi_span(f'下一步 {index}', f'Next {index}')}" + f"

    {bi_item(str(item.get('title', '升级方向')))}

    " + f"

    {bi_item(str(item.get('why', '提升复用稳定性。')))}

    " + f"{actions}" + f"

    {bi_item(str(item.get('unlocks', '')))}

    " + "
    " + ) + return "".join(blocks) + + +def render_html(summary: dict) -> str: + charts = render_chart_set(summary) + nav_html = render_report_nav(REPORT_NAV_V2) + language_switch = render_language_switch() + skill = summary.get("skill_summary", {}) + metadata = summary.get("metadata", {}) + scorecard = summary.get("scorecard", {}) + profile = summary.get("capability_profile", {}) + contract = summary.get("contract_boundary", {}) + quality = summary.get("quality_review", {}) + risk = summary.get("risk_governance", {}) + world_readiness = summary.get("world_class_readiness", {}) + assets = summary.get("package_assets", {}) + roadmap = summary.get("iteration_roadmap", {}) + output_execution = summary.get("output_execution", {}) + output_execution_summary = output_execution.get("summary", {}) + output_review = summary.get("output_review_adjudication", {}) + output_review_summary = output_review.get("summary", {}) + report_contract = summary.get("report_contract", {}) + html_report_path = str(report_contract.get("html_report") or "reports/skill-overview.html") + open_report_message = f"创建完成后建议先打开 {html_report_path},再继续扩展包体。" + hero_meta = [ + (f"技能名称:{summary['name']}", f"Skill name: {summary['name']}"), + (f"成熟度:{mode_zh(metadata.get('maturity_tier', 'scaffold'))}", f"Maturity: {metadata.get('maturity_tier', 'scaffold')}"), + (f"格式:{mode_zh(metadata.get('canonical_format', 'agent-skills'))}", f"Format: {metadata.get('canonical_format', 'agent-skills')}"), + (f"更新时间:{metadata.get('updated_at', '')}", f"Updated: {metadata.get('updated_at', '')}"), + ] + hero_meta_html = "".join(f"{bi_span(zh, en)}" for zh, en in hero_meta) + target_badges = "".join(f"{bi_span(str(target), str(target))}" for target in metadata.get("targets", [])) + score_strip = render_score_strip(scorecard) + package_rows = "".join( + ( + "" + f"{html.escape(str(item.get('path', '')))}" + f"{bi_span(PACKAGE_LABEL_ZH.get(str(item.get('path', '')), str(item.get('label', ''))), str(item.get('label', '')))}" + f"{bi_span(KIND_ZH.get(str(item.get('kind', '')), str(item.get('kind', ''))), str(item.get('kind', '')))}" + "" + ) + for item in assets.get("entries", []) + ) + quality_rows = render_audit_rows( + [{"name": "强项", "signal": item, "response": "保留并复用"} for item in quality.get("strengths", [])[:3]] + + [{"name": "缺口", "signal": item, "response": "纳入下一轮修复"} for item in quality.get("gaps", [])[:3]] + ) + if output_review_summary: + agreement = output_review_summary.get("agreement_rate") + review_items = [ + f"评审进度:{output_review_summary.get('judgment_count', 0)} / {output_review_summary.get('pair_count', 0)}", + f"待评审:{output_review_summary.get('pending_count', 0)}", + f"一致率:{agreement if agreement is not None else '暂无'}", + f"非法决策:{output_review_summary.get('invalid_decision_count', 0)}", + ] + else: + review_items = ["尚未生成盲评审定报告。"] + if output_execution_summary: + execution_items = [ + f"变体运行:{output_execution_summary.get('variant_run_count', 0)}", + f"模型执行:{output_execution_summary.get('model_executed_count', 0)}", + f"记录样本:{output_execution_summary.get('recorded_fixture_count', 0)}", + f"Token 估算:{output_execution_summary.get('token_estimated_count', 0)}", + ] + else: + execution_items = ["尚未生成输出执行证据报告。"] + capability_items = [ + f"能力类型:{profile.get('task_family', 'Skill workflow')}", + f"成熟度:{profile.get('maturity', 'scaffold')}", + f"触发强度:{profile.get('trigger_strength', 'manual')}", + f"复用范围:{profile.get('reuse_scope', '本地复用')}", + ] + trigger = contract.get("trigger", {}) + risk_rows = render_audit_rows(risk.get("risks", [])) + + return f""" + + + + + {html.escape(summary['name'])} Skill 生成审计报告 + + + + +
    + +
    + + {language_switch} +
    +
    +
    +
    +
    +
    +

    {bi_span("YAO Skill 生成审计报告", "YAO Skill Generation Audit")}

    +

    {html.escape(summary['name'])}

    +

    {bi_span("技能审计报告", f"{summary['display_name']} Audit Report")}

    +

    {bi_span("这份报告默认使用中文简体,把新 Skill 的定位、指标、原理、契约、质量、风险、资产和迭代路线整理为一份可审计的 HTML 报告。", en_for(str(summary["description"])))}

    +
    {hero_meta_html}
    +
    {target_badges}
    +
    + +
    +
    {score_strip}
    +
    + +
    +
    +
    +

    {bi_span("技能概述", "Overview")}

    +

    {bi_span("先用一屏说明这个 Skill 是什么、给谁用、交付什么。", "A first-screen explanation of what this skill is, who it serves, and what it delivers.")}

    +
    +
    +
    +

    {bi_span("作用定位", "Role")}

    + {render_list([summary["description"], skill.get("core_value", ""), f"交付结果:{', '.join(skill.get('deliverables', []))}"])} +
    + {charts["flow"]} +
    +
    +
    + +
    +
    +
    +

    {bi_span("总览指标", "Metrics")}

    +

    {bi_span("分数来自本地文件和 reports 证据,缺失时明确标为证据不足。", "Scores are derived from package files and reports; missing inputs are shown as evidence gaps.")}

    +
    +
    +

    {bi_span("指标判读", "Reading")}

    +

    {bi_span("先看雷达图判断能力短板,再看每项分数的证据原因。分数不是装饰数字,必须和本地文件、reports 证据或证据不足提示对应。", "Read the radar first for weak spots, then inspect each score with its evidence. Scores must map to local files, reports, or explicit evidence gaps.")}

    +
    +
    +
    +
    +
    + {charts["radar"]} +
    +

    {bi_span("成熟度条", "Maturity Bar")}

    +

    {bi_span("这里把每个指标压缩为状态、分数和第一条证据,先给出整体判断,再进入下方明细。", "This compresses each metric into status, score, and the first evidence item before the detailed cards below.")}

    + {render_metric_summary(scorecard)} +
    +
    +
    +
    {bi_span("指标明细", "Metric Details")}
    +
    {render_metric_cards(scorecard)}
    +
    +
    +
    +
    + +
    +
    +
    +

    {bi_span("能力画像", "Capability")}

    +

    {bi_span("判断这个 Skill 在能力地图中的位置和复用范围。", "Places this skill on a capability map and clarifies reuse scope.")}

    +
    +
    + {charts["matrix"]} +
    +

    {bi_span("画像摘要", "Profile")}

    + {render_list(capability_items)} +
    +
    +
    +
    + +
    +
    +
    +

    {bi_span("原理结构", "Principle")}

    +

    {bi_span("说明入口、参考、脚本、评估和报告如何组成一个稳定闭环。", "Explains how entrypoint, references, scripts, evals, and reports form a stable loop.")}

    +
    +
    +
    {charts["layers"]}
    +
    +
    +

    {bi_span("执行流程", "Execution Flow")}

    + {render_ordered_steps(summary.get("logic_steps", []))} +
    +
    +

    {bi_span("调用方式", "How To Use")}

    + {render_ordered_steps(summary.get("usage_steps", []))} +
    +
    +
    +
    +
    + +
    +
    +
    +

    {bi_span("契约边界", "Contract")}

    +

    {bi_span("把触发、输入、输出和排除场景放在同一屏。", "Keeps trigger, inputs, outputs, and exclusions on the same screen.")}

    +
    +
    +
    +

    {bi_span("触发描述", "Trigger")}

    +

    {bi_span(readable_description_zh(str(trigger.get("description", ""))), en_for(str(trigger.get("description", ""))))}

    +

    {bi_span("输入材料", "Inputs")}

    + {render_list(contract.get("inputs", []))} +
    +
    +

    {bi_span("输出结果", "Outputs")}

    + {render_list(contract.get("outputs", []))} +

    {bi_span("不应触发", "Should Not Trigger")}

    + {render_list(contract.get("should_not_trigger", []))} +
    +
    +
    +
    + +
    +
    +
    +

    {bi_span("质量评估", "Quality")}

    +

    {bi_span("展示强项、缺口和建议,避免只给分不解释。", "Shows strengths, gaps, and recommendations instead of scores without explanation.")}

    +
    +
    + + + {quality_rows} +
    {bi_span("类型", "Type")}{bi_span("证据", "Evidence")}{bi_span("建议", "Action")}
    +
    +
    +

    {bi_span("执行证据", "Execution Evidence")}

    + {render_list(execution_items)} +
    +
    +

    {bi_span("盲评审定", "Blind Adjudication")}

    + {render_list(review_items)} +
    +
    +
    +
    +

    {bi_span("评审原则", "Review Rule")}

    + {render_list(["先记录 reviewer 对 A/B 的选择,再打开答案 key 计算一致率。", "缺少真实 reviewer 决策时只显示待评审,不伪造人工结论。"])} +
    +
    +

    {bi_span("运行原则", "Run Rule")}

    + {render_list(["recorded fixture 只能证明可复现样本,不等同于模型执行。", "只有 provider runner 返回 model metadata 时才计入 model-executed。"])} +
    +
    +
    +
    +
    + +
    +
    +
    +

    {bi_span("风险治理", "Risk")}

    +

    {bi_span("提前暴露误触发、漂移、证据不足和迁移风险。", "Surfaces trigger, drift, evidence, and portability risks before the package grows.")}

    +
    +
    + {charts["risk_heatmap"]} +
    + + + {risk_rows} +
    {bi_span("风险", "Risk")}{bi_span("信号", "Signal")}{bi_span("应对", "Response")}
    +
    +
    + {render_world_class_readiness(world_readiness)} +
    +
    + +
    +
    +
    +

    {bi_span("包体资产", "Assets")}

    +

    {bi_span("让 reviewer 快速确认关键文件、目录和资产分布。", "Lets reviewers confirm key files, directories, and asset distribution quickly.")}

    +
    +
    + {charts["asset_donut"]} + + + {package_rows} +
    {bi_span("路径", "Path")}{bi_span("作用", "Role")}{bi_span("类型", "Type")}
    +
    +
    +
    + +
    +
    +
    +

    {bi_span("迭代路线", "Roadmap")}

    +

    {bi_span("把下一步升级收束为少数高价值动作。", "Keeps next iteration moves focused and actionable.")}

    +
    +
    +
    {charts["timeline"]}
    +
    {render_roadmap(roadmap.get("items", []))}
    +
    +
    +
    +
    + + + +""" + + +def render_skill_overview(skill_dir: Path, output_html: Path | None = None, output_json: Path | None = None) -> dict: + 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 / "skill-overview.html" + output_json = output_json or reports_dir / "skill-overview.json" + + summary = build_report_model(skill_dir) + output_html.write_text(render_html(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8") + + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "html": str(output_html), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render the HTML skill report 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() + + result = render_skill_overview( + Path(args.skill_dir), + Path(args.output_html).resolve() if args.output_html else None, + Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_social_preview.py b/scripts/render_social_preview.py new file mode 100644 index 0000000..73469a5 --- /dev/null +++ b/scripts/render_social_preview.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from pathlib import Path + + +WIDTH = 1280 +HEIGHT = 640 +BACKGROUND = "#FFFFFF" +INK = "#111111" +MUTED = "#4B5563" +ACCENT = "#D9DDE3" + +ROOT = Path(__file__).resolve().parents[1] +OUT_DIR = ROOT / "docs" / "assets" +PNG_PATH = OUT_DIR / "social-preview.png" +SVG_PATH = OUT_DIR / "social-preview.svg" + + +SERIF = "/System/Library/Fonts/Supplemental/Georgia.ttf" +SANS = "/System/Library/Fonts/SFNS.ttf" + + +def draw_png() -> None: + try: + from PIL import Image, ImageDraw, ImageFont + except ImportError as exc: + raise SystemExit("Pillow is required to render PNG social previews.") from exc + + def font(path: str, size: int): + return ImageFont.truetype(path, size) + + OUT_DIR.mkdir(parents=True, exist_ok=True) + image = Image.new("RGB", (WIDTH, HEIGHT), BACKGROUND) + draw = ImageDraw.Draw(image) + + title_font = font(SERIF, 74) + headline_font = font(SANS, 34) + body_font = font(SANS, 21) + stat_font = font(SANS, 18) + small_font = font(SANS, 16) + + x = 92 + y = 82 + + draw.text((x, y), "YAO", fill=INK, font=title_font) + y += 90 + draw.text((x, y), "Yielding AI Outcomes", fill=INK, font=headline_font) + y += 60 + draw.text( + (x, y), + "A rigorous engineering, evaluation, governance, and portability system", + fill=INK, + font=body_font, + ) + y += 34 + draw.text( + (x, y), + "for reusable agent skills.", + fill=INK, + font=body_font, + ) + + y += 62 + draw.line((x, y, WIDTH - 92, y), fill=ACCENT, width=2) + y += 34 + + score_box_w = 250 + score_box_h = 118 + draw.rounded_rectangle( + (x, y, x + score_box_w, y + score_box_h), + radius=18, + outline=ACCENT, + width=2, + fill=BACKGROUND, + ) + draw.text((x + 22, y + 18), "Weighted Score", fill=MUTED, font=stat_font) + draw.text((x + 22, y + 48), "91.5", fill=INK, font=font(SERIF, 42)) + draw.text((x + 124, y + 62), "/ 100", fill=MUTED, font=stat_font) + draw.text( + (x + 22, y + 88), + "Method 9.5 · Eval 9.5 · Governance 9.5", + fill=MUTED, + font=small_font, + ) + + pillar_x = x + score_box_w + 36 + pillar_y = y + 4 + pillars = [ + ("Engineering", "Unified CLI, CI, validation, reporting, and packaging."), + ("Evaluation", "Holdouts, route confusion, judge-backed blind evals."), + ("Governance", "Lifecycle, maturity, promotion, and review evidence."), + ("Portability", "Neutral metadata, adapters, contracts, and degradation."), + ] + + box_w = 400 + box_h = 68 + gap_x = 24 + gap_y = 18 + for idx, (label, desc) in enumerate(pillars): + col = idx % 2 + row = idx // 2 + bx = pillar_x + col * (box_w + gap_x) + by = pillar_y + row * (box_h + gap_y) + draw.rounded_rectangle( + (bx, by, bx + box_w, by + box_h), + radius=16, + outline=ACCENT, + width=2, + fill=BACKGROUND, + ) + draw.text((bx + 18, by + 14), label, fill=INK, font=stat_font) + draw.text((bx + 18, by + 38), desc, fill=MUTED, font=small_font) + + footer_y = HEIGHT - 84 + draw.line((x, footer_y - 26, WIDTH - 92, footer_y - 26), fill=ACCENT, width=2) + draw.text( + (x, footer_y), + "Reusable agent skills with explicit boundaries, strict evaluation, visible governance, and local reliability.", + fill=MUTED, + font=small_font, + ) + + image.save(PNG_PATH) + + +def draw_svg() -> None: + OUT_DIR.mkdir(parents=True, exist_ok=True) + svg = f""" + + YAO + Yielding AI Outcomes + A rigorous engineering, evaluation, governance, and portability system + for reusable agent skills. + + + Weighted Score + 91.5 + / 100 + Method 9.5 · Eval 9.5 · Governance 9.5 + + + Engineering + Unified CLI, CI, validation, reporting, and packaging. + + + Evaluation + Holdouts, route confusion, judge-backed blind evals. + + + Governance + Lifecycle, maturity, promotion, and review evidence. + + + Portability + Neutral metadata, adapters, contracts, and degradation. + + + + Reusable agent skills with explicit boundaries, strict evaluation, visible governance, and local reliability. + +""" + SVG_PATH.write_text(svg, encoding="utf-8") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render the repository social preview image assets.") + parser.add_argument( + "--format", + choices=["all", "png", "svg"], + default="all", + help="Asset format to render.", + ) + args = parser.parse_args() + + if args.format in {"all", "png"}: + draw_png() + print(f"Wrote {PNG_PATH}") + if args.format in {"all", "svg"}: + draw_svg() + print(f"Wrote {SVG_PATH}") + + +if __name__ == "__main__": + main() diff --git a/scripts/render_system_model.py b/scripts/render_system_model.py new file mode 100644 index 0000000..a202c7e --- /dev/null +++ b/scripts/render_system_model.py @@ -0,0 +1,481 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def load_json(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def as_list(value) -> list[str]: + if not value: + return [] + if isinstance(value, list): + return [str(item).strip() for item in value if str(item).strip()] + return [str(value).strip()] + + +def compact_unique(items: list[str], limit: int = 6) -> list[str]: + seen = set() + result = [] + for item in items: + normalized = item.strip() + if not normalized or normalized.lower() in seen: + continue + seen.add(normalized.lower()) + result.append(normalized) + if len(result) >= limit: + break + return result + + +def load_context(skill_dir: Path) -> dict: + intent = load_json(skill_dir / "reports" / "intent-confidence.json") + return intent.get("context", {}) if isinstance(intent, dict) else {} + + +def intent_summary(intent: dict) -> dict: + if not isinstance(intent, dict): + return {} + summary = intent.get("summary") + return summary if isinstance(summary, dict) else intent + + +def output_risk_labels(profile: dict) -> list[str]: + return [ + str(item.get("label", item.get("key", ""))) + for item in profile.get("risk_families", []) + if item.get("label") or item.get("key") + ][:5] + + +def reference_patterns(synthesis: dict) -> list[str]: + payload = synthesis.get("synthesis", {}) if isinstance(synthesis, dict) else {} + recommendation = payload.get("recommendation", {}) if isinstance(payload, dict) else {} + return compact_unique( + as_list(recommendation.get("borrow_now")) + + as_list(payload.get("borrow_now")) + + as_list(recommendation.get("avoid_for_now")) + + as_list(payload.get("avoid_now")), + limit=5, + ) + + +def safe_int(value, default: int = 100) -> int: + try: + return int(value) + except (TypeError, ValueError): + return default + + +def build_boundary_map(description: str, context: dict, manifest: dict) -> dict: + job = context.get("job") or description + primary_output = context.get("primary_output") or "a reusable skill output" + real_inputs = as_list(context.get("real_inputs")) or ["user-provided workflow notes, prompts, docs, or examples"] + exclusions = as_list(context.get("exclusions")) or [ + "one-off adjacent requests that do not match the recurring job", + "private local material that was not intentionally included", + ] + constraints = as_list(context.get("constraints")) + standards = as_list(context.get("standards")) + maturity = manifest.get("maturity_tier", "scaffold") + return { + "owned_job": str(job), + "input_boundary": real_inputs, + "output_boundary": str(primary_output), + "non_goals": exclusions, + "constraints": constraints, + "standards": standards, + "human_judgment_boundary": [ + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby.", + ], + "maturity_assumption": maturity, + } + + +def build_feedback_loops(context: dict, intent: dict, output_profile: dict, synthesis: dict) -> list[dict]: + confidence = intent_summary(intent).get("score", 0) + risks = output_risk_labels(output_profile) + patterns = reference_patterns(synthesis) + return [ + { + "name": "Intent boundary loop", + "signal": f"Intent confidence score is {confidence}/100.", + "response": "Ask only the highest-leverage clarification before adding package weight.", + "evidence": "reports/intent-confidence.md and reports/intent-dialogue.md", + }, + { + "name": "Reference synthesis loop", + "signal": "Benchmark patterns are useful only after they are abstracted into borrow and avoid guidance.", + "response": "Borrow one pattern at a time and keep the rest as reviewer-visible evidence.", + "evidence": "reports/reference-synthesis.md", + "current_patterns": patterns, + }, + { + "name": "Output quality loop", + "signal": "Generated output may fail in recurring domain-specific ways.", + "response": "Apply predicted output-risk families as self-repair checks before final output.", + "evidence": "reports/output-risk-profile.md", + "current_risk_families": risks, + }, + { + "name": "Reviewer feedback loop", + "signal": "Human review catches drift that static checks miss.", + "response": "Capture lightweight feedback and turn repeated findings into gates or references.", + "evidence": "reports/review-viewer.html and feedback records", + }, + { + "name": "Lifecycle loop", + "signal": "As reuse grows, the skill needs stronger gates, ownership, and regression evidence.", + "response": "Promote only when the next gate improves reliability more than context cost.", + "evidence": "manifest.json, reports/iteration-directions.md, and governance checks", + }, + ] + + +def build_drift_watch(manifest: dict, output_profile: dict) -> list[dict]: + maturity = manifest.get("maturity_tier", "scaffold") + risk_labels = output_risk_labels(output_profile) + return [ + { + "name": "Trigger drift", + "watch_signal": "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "countermeasure": "Add near-neighbor exclusions and route evals before expanding workflow steps.", + "cadence": "per trigger or description change", + }, + { + "name": "Output drift", + "watch_signal": "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "countermeasure": "Refresh output-risk and artifact-design profiles, then add one self-repair check.", + "cadence": "after the first 3-5 real uses", + "risk_families": risk_labels, + }, + { + "name": "Reference drift", + "watch_signal": "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "countermeasure": "Re-run reference synthesis and keep only patterns that improve the current boundary.", + "cadence": "per material benchmark or product assumption change", + }, + { + "name": "Governance drift", + "watch_signal": "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal.", + "countermeasure": "Promote maturity tier and add reviewer-visible lifecycle evidence.", + "cadence": "monthly" if maturity in {"production", "governed", "library"} else "when reuse becomes real", + }, + ] + + +def build_failure_map(output_profile: dict, prompt_profile: dict) -> list[dict]: + risks = output_risk_labels(output_profile) + prompt_matrix = prompt_profile.get("quality_matrix", []) if isinstance(prompt_profile, dict) else [] + weak_prompt_axes = [ + str(item.get("label", "Prompt quality")) + for item in prompt_matrix + if isinstance(item, dict) and safe_int(item.get("score"), 100) < 80 + ][:3] + return [ + { + "family": "Boundary failure", + "symptom": "The skill handles nearby requests that were never part of the recurring job.", + "repair": "Narrow the description and add explicit non-goals before adding more execution steps.", + }, + { + "family": "Feedback gap", + "symptom": "The skill has rules but no signal telling authors which rule should change after use.", + "repair": "Turn repeated reviewer feedback into one eval, one reference note, or one self-repair check.", + }, + { + "family": "Output degradation", + "symptom": "The result is structurally correct but generic, cluttered, or weakly matched to the user's domain.", + "repair": "Use output-risk families as pre-final checks.", + "current_risk_families": risks, + }, + { + "family": "Prompt-behavior mismatch", + "symptom": "The role, task, and format are copied from a prompt instead of becoming stable skill behavior.", + "repair": "Convert reusable role/task/format assumptions into workflow, reports, or references.", + "watch_axes": weak_prompt_axes, + }, + ] + + +def build_leverage_points(intent: dict, manifest: dict, output_profile: dict, synthesis: dict) -> list[dict]: + confidence = intent_summary(intent).get("score", 0) + maturity = manifest.get("maturity_tier", "scaffold") + risks = output_risk_labels(output_profile) + patterns = reference_patterns(synthesis) + points = [] + if confidence < 80: + points.append( + { + "rank": 1, + "point": "Clarify the real job boundary", + "why": "Intent uncertainty creates downstream trigger, output, and governance errors.", + "move": "Ask one focused question and update intent context before adding assets.", + } + ) + points.append( + { + "rank": 2, + "point": "Tune the frontmatter description", + "why": "The description is the highest-leverage routing surface.", + "move": "Name the recurring job, expected input, output, and strongest non-goal in compact language.", + } + ) + if risks: + points.append( + { + "rank": 3, + "point": "Install output self-repair checks", + "why": f"The likely failure families are: {', '.join(risks[:3])}.", + "move": "Add only the checks that prevent recurring output mistakes.", + } + ) + if patterns: + points.append( + { + "rank": 4, + "point": "Borrow one pattern, not a whole product", + "why": "External references improve quality when reduced to structure, not copied as surface style.", + "move": f"Start from: {patterns[0]}", + } + ) + if maturity in {"production", "governed", "library"}: + points.append( + { + "rank": 5, + "point": "Close the lifecycle loop", + "why": "Team-reused skills need visible ownership, review cadence, and regression evidence.", + "move": "Keep manifest, review viewer, and iteration directions aligned after each material change.", + } + ) + return points[:5] + + +def stability_score(intent: dict, manifest: dict, output_profile: dict, synthesis: dict) -> tuple[int, str]: + confidence = intent_summary(intent).get("score", 0) + maturity = manifest.get("maturity_tier", "scaffold") + score = 55 + score += min(20, int(confidence / 5)) + score += 8 if output_profile.get("risk_families") else 0 + score += 8 if reference_patterns(synthesis) else 0 + score += 9 if maturity in {"production", "governed", "library"} else 4 + score = max(0, min(100, score)) + if score >= 90: + band = "system-ready" + elif score >= 75: + band = "stable-first-pass" + elif score >= 60: + band = "needs-boundary-tuning" + else: + band = "fragile" + return score, band + + +def build_system_model(skill_dir: Path) -> dict: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _body = parse_frontmatter(skill_text) + manifest = load_json(skill_dir / "manifest.json") + intent = load_json(skill_dir / "reports" / "intent-confidence.json") + context = load_context(skill_dir) + synthesis = load_json(skill_dir / "reports" / "reference-synthesis.json") + output_profile = load_json(skill_dir / "reports" / "output-risk-profile.json") + prompt_profile = load_json(skill_dir / "reports" / "prompt-quality-profile.json") + description = str(frontmatter.get("description", "")) + score, band = stability_score(intent, manifest, output_profile, synthesis) + return { + "skill_name": frontmatter.get("name", skill_dir.name), + "description": description, + "systems_doctrine": "Structure drives behavior: improve the boundary, feedback loops, drift watch, and leverage points before adding weight.", + "stability": { + "score": score, + "band": band, + }, + "boundary_map": build_boundary_map(description, context, manifest), + "feedback_loops": build_feedback_loops(context, intent, output_profile, synthesis), + "drift_watch": build_drift_watch(manifest, output_profile), + "failure_pattern_map": build_failure_map(output_profile, prompt_profile), + "leverage_points": build_leverage_points(intent, manifest, output_profile, synthesis), + "reviewer_rule": "Reviewer should ask whether the skill's structure will keep producing the desired behavior after repeated real use.", + } + + +def render_markdown(model: dict) -> str: + boundary = model["boundary_map"] + lines = [ + "# System Model", + "", + f"Skill: `{model['skill_name']}`", + "", + f"- Stability score: `{model['stability']['score']}/100`", + f"- Stability band: `{model['stability']['band']}`", + f"- Doctrine: {model['systems_doctrine']}", + "", + "## System Boundary Map", + "", + f"- Owned job: {boundary['owned_job']}", + f"- Output boundary: {boundary['output_boundary']}", + f"- Maturity assumption: `{boundary['maturity_assumption']}`", + "- Input boundary:", + ] + lines.extend(f" - {item}" for item in boundary["input_boundary"]) + lines.append("- Non-goals:") + lines.extend(f" - {item}" for item in boundary["non_goals"]) + if boundary["constraints"]: + lines.append("- Constraints:") + lines.extend(f" - {item}" for item in boundary["constraints"]) + if boundary["standards"]: + lines.append("- Standards:") + lines.extend(f" - {item}" for item in boundary["standards"]) + lines.append("- Human judgment boundary:") + lines.extend(f" - {item}" for item in boundary["human_judgment_boundary"]) + + lines.extend(["", "## Feedback Loops", ""]) + for loop in model["feedback_loops"]: + lines.extend( + [ + f"### {loop['name']}", + "", + f"- Signal: {loop['signal']}", + f"- Response: {loop['response']}", + f"- Evidence: {loop['evidence']}", + ] + ) + if loop.get("current_patterns"): + lines.append("- Current patterns:") + lines.extend(f" - {item}" for item in loop["current_patterns"]) + if loop.get("current_risk_families"): + lines.append("- Current risk families:") + lines.extend(f" - {item}" for item in loop["current_risk_families"]) + lines.append("") + + lines.extend(["## Delay And Drift Watch", ""]) + for item in model["drift_watch"]: + lines.extend( + [ + f"### {item['name']}", + "", + f"- Watch signal: {item['watch_signal']}", + f"- Countermeasure: {item['countermeasure']}", + f"- Cadence: {item['cadence']}", + ] + ) + if item.get("risk_families"): + lines.append("- Risk families:") + lines.extend(f" - {risk}" for risk in item["risk_families"]) + lines.append("") + + lines.extend(["## Failure Pattern Map", ""]) + for item in model["failure_pattern_map"]: + lines.extend( + [ + f"### {item['family']}", + "", + f"- Symptom: {item['symptom']}", + f"- Repair: {item['repair']}", + ] + ) + for key in ("current_risk_families", "watch_axes"): + if item.get(key): + label = key.replace("_", " ").title() + lines.append(f"- {label}:") + lines.extend(f" - {value}" for value in item[key]) + lines.append("") + + lines.extend(["## Highest Leverage Moves", ""]) + for point in model["leverage_points"]: + lines.extend( + [ + f"### {point['rank']}. {point['point']}", + "", + f"- Why: {point['why']}", + f"- Move: {point['move']}", + "", + ] + ) + + lines.extend( + [ + "## Reviewer Use", + "", + f"- {model['reviewer_rule']}", + "- Prefer changing the system boundary, feedback loop, or leverage point before adding more prose.", + "- If a problem repeats, convert it into a named failure pattern and one regression check.", + "", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def render_system_model(skill_dir: Path, output_md: Path | None = None, output_json: Path | None = None) -> dict: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "system-model.md" + output_json = output_json or reports_dir / "system-model.json" + model = build_system_model(skill_dir) + output_md.write_text(render_markdown(model), encoding="utf-8") + output_json.write_text(json.dumps(model, ensure_ascii=False, indent=2), encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": { + "skill_name": model["skill_name"], + "stability": model["stability"], + "leverage_points": model["leverage_points"][:3], + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a systems-thinking model for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + result = render_system_model( + Path(args.skill_dir), + Path(args.output_md).resolve() if args.output_md else None, + Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_telemetry_hook_recipes.py b/scripts/render_telemetry_hook_recipes.py new file mode 100644 index 0000000..cafee93 --- /dev/null +++ b/scripts/render_telemetry_hook_recipes.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python3 +import argparse +import json +import shlex +from pathlib import Path +from typing import Any + +from render_adoption_drift_report import SENSITIVE_FIELDS, display_path + + +SCHEMA_VERSION = "1.0" +DEFAULT_RECIPES = [ + { + "id": "browser-extension", + "client": "Browser extension", + "command": "browser-extension", + "event": "skill_activation", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "trigger_points": [ + "Skill is explicitly selected by the user.", + "Skill activation is accepted by the host client.", + ], + }, + { + "id": "chrome-extension", + "client": "Chrome extension", + "command": "chrome-extension", + "event": "skill_output", + "activation_type": "manual", + "outcome": "edited", + "failure_type": "none", + "trigger_points": [ + "User keeps or edits a skill-generated artifact.", + "Extension records only the final outcome class, not page content.", + ], + }, + { + "id": "vscode-extension", + "client": "VS Code extension", + "command": "vscode-extension", + "event": "skill_activation", + "activation_type": "implicit", + "outcome": "accepted", + "failure_type": "none", + "trigger_points": [ + "Workspace skill route chooses this skill.", + "Extension records route outcome without file paths or code snippets.", + ], + }, + { + "id": "cli-wrapper", + "client": "CLI wrapper", + "command": "cli-wrapper", + "event": "script_run", + "activation_type": "manual", + "outcome": "unknown", + "failure_type": "none", + "trigger_points": [ + "Wrapper starts or finishes a known skill workflow command.", + "Wrapper records command family only, never command arguments.", + ], + }, + { + "id": "provider-adapter", + "client": "Provider adapter", + "command": "provider-adapter", + "event": "skill_output", + "activation_type": "manual", + "outcome": "accepted", + "failure_type": "none", + "trigger_points": [ + "Provider adapter receives an accepted or rejected skill output signal.", + "Adapter records only normalized outcome metadata.", + ], + }, +] + + +def default_spool(skill_dir: Path) -> Path: + return skill_dir / ".yao" / "telemetry_spool" / "external_events.jsonl" + + +def command_text(argv: list[str]) -> str: + return shlex.join(argv) + + +def emit_argv(skill_dir: Path, output_jsonl: Path, recipe: dict[str, Any], dry_run: bool = False) -> list[str]: + argv = [ + "python3", + "scripts/yao.py", + "telemetry-emit", + display_path(skill_dir), + "--output-jsonl", + display_path(output_jsonl), + "--event", + recipe["event"], + "--activation-type", + recipe["activation_type"], + "--outcome", + recipe["outcome"], + "--failure-type", + recipe["failure_type"], + "--command", + recipe["command"], + ] + if dry_run: + argv.append("--dry-run") + return argv + + +def import_argv(skill_dir: Path, output_jsonl: Path) -> list[str]: + return [ + "python3", + "scripts/yao.py", + "telemetry-import", + display_path(skill_dir), + "--input-jsonl", + display_path(output_jsonl), + ] + + +def build_recipe(skill_dir: Path, output_jsonl: Path, recipe: dict[str, Any]) -> dict[str, Any]: + emit = emit_argv(skill_dir, output_jsonl, recipe) + dry_run = emit_argv(skill_dir, output_jsonl, recipe, dry_run=True) + return { + **recipe, + "source": "external", + "native_auto_capture": False, + "integration_status": "client-hook-recipe", + "metadata_only": True, + "emit_argv": emit, + "emit_command": command_text(emit), + "dry_run_argv": dry_run, + "dry_run_command": command_text(dry_run), + } + + +def render_markdown(report: dict[str, Any]) -> str: + lines = [ + "# Telemetry Hook Recipes", + "", + "These recipes show how Browser, Chrome, IDE, wrapper, or provider-side integrations can call `telemetry-emit` without collecting raw prompts, outputs, transcripts, messages, notes, arguments, or file content.", + "", + "They are client hook recipes, not proof that a host client is already natively integrated.", + "", + "## Summary", + "", + f"- Recipe count: `{report['summary']['recipe_count']}`", + f"- Native auto-capture integrations claimed: `{report['summary']['native_auto_capture_count']}`", + f"- Default spool: `{report['artifacts']['spool_jsonl']}`", + "", + "## Recipes", + "", + "| Client | Command | Event | Outcome | Dry run |", + "| --- | --- | --- | --- | --- |", + ] + for recipe in report["recipes"]: + lines.append( + f"| {recipe['client']} | `{recipe['command']}` | `{recipe['event']}` | `{recipe['outcome']}` | `{recipe['dry_run_command']}` |" + ) + lines.extend( + [ + "", + "## Import", + "", + "After a client finishes a batch, import the local spool:", + "", + "```bash", + report["artifacts"]["import_command"], + "```", + "", + "## Privacy Contract", + "", + "- Allowed fields: `event`, `skill`, `version`, `source`, `command`, `activation_type`, `outcome`, `failure_type`, `timestamp`.", + f"- Blocked raw-content fields: `{', '.join(report['privacy_contract']['blocked_fields'])}`.", + "- Client integrations must map local state to normalized outcome classes before calling the hook.", + ] + ) + return "\n".join(lines) + "\n" + + +def render_recipes( + skill_dir: Path, + output_json: Path | None = None, + output_md: Path | None = None, + output_jsonl: Path | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + output_json = (output_json or reports_dir / "telemetry_hook_recipes.json").resolve() + output_md = (output_md or reports_dir / "telemetry_hook_recipes.md").resolve() + output_jsonl = (output_jsonl or default_spool(skill_dir)).resolve() + recipes = [build_recipe(skill_dir, output_jsonl, recipe) for recipe in DEFAULT_RECIPES] + import_cmd = import_argv(skill_dir, output_jsonl) + report = { + "ok": True, + "schema_version": SCHEMA_VERSION, + "skill_dir": display_path(skill_dir), + "summary": { + "recipe_count": len(recipes), + "native_auto_capture_count": sum(1 for recipe in recipes if recipe["native_auto_capture"]), + "metadata_only_recipe_count": sum(1 for recipe in recipes if recipe["metadata_only"]), + }, + "privacy_contract": { + "raw_content_allowed": False, + "blocked_fields": sorted(SENSITIVE_FIELDS), + "allowed_fields": [ + "event", + "skill", + "version", + "source", + "command", + "activation_type", + "outcome", + "failure_type", + "timestamp", + ], + }, + "recipes": recipes, + "artifacts": { + "json": display_path(output_json), + "markdown": display_path(output_md), + "spool_jsonl": display_path(output_jsonl), + "import_command": command_text(import_cmd), + "import_argv": import_cmd, + }, + "warnings": [ + "These recipes do not prove native host-client integration; they define the local metadata-only hook contract for those integrations." + ], + "failures": [], + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render metadata-only telemetry client hook recipes.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--output-jsonl") + args = parser.parse_args() + report = render_recipes( + Path(args.skill_dir), + output_json=Path(args.output_json) if args.output_json else None, + output_md=Path(args.output_md) if args.output_md else None, + output_jsonl=Path(args.output_jsonl) if args.output_jsonl else None, + ) + print(json.dumps(report, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_weekly_curator_report.py b/scripts/render_weekly_curator_report.py new file mode 100644 index 0000000..06b4092 --- /dev/null +++ b/scripts/render_weekly_curator_report.py @@ -0,0 +1,433 @@ +#!/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() diff --git a/scripts/render_world_class_claim_guard.py b/scripts/render_world_class_claim_guard.py new file mode 100644 index 0000000..b809caf --- /dev/null +++ b/scripts/render_world_class_claim_guard.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from datetime import date +from pathlib import Path +from typing import Any + +from render_world_class_evidence_ledger import build_ledger + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Scans public claim surfaces so world-class completion is not claimed before accepted evidence exists." + +FORBIDDEN_WHEN_PENDING = [ + { + "key": "ready-to-claim-true", + "pattern": re.compile(r"ready[\s_-]*to[\s_-]*claim[\s_-]*world[\s_-]*class\s*[:=]\s*`?true`?", re.IGNORECASE), + "reason": "ready_to_claim_world_class cannot be true while ledger evidence is pending.", + }, + { + "key": "world-class-ready-true", + "pattern": re.compile(r"(public\s+)?world[\s_-]*class[\s_-]*ready\s*[:=]\s*`?true`?", re.IGNORECASE), + "reason": "world-class readiness cannot be claimed before accepted external and human evidence exists.", + }, + { + "key": "json-world-class-ready-true", + "pattern": re.compile(r'"(?:ready_to_claim_world_class|world_class_ready|public_world_class_ready)"\s*:\s*true', re.IGNORECASE), + "reason": "machine-readable claim fields must remain false until the ledger is ready.", + }, + { + "key": "completion-phrase", + "pattern": re.compile(r"(public\s+)?world[\s_-]*class\s+(?:complete|completed|achieved|certified)", re.IGNORECASE), + "reason": "completion language is blocked until the world-class ledger is accepted.", + }, + { + "key": "zh-completion-phrase", + "pattern": re.compile(r"世界级(?:已完成|完成|已达成|达成|准备就绪|可声明|认证通过)"), + "reason": "中文完成态表述必须等到 ledger ready 后才能出现。", + }, +] + + +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 rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def resolve_claim_surface(raw_path: str, skill_dir: Path) -> Path: + path = Path(raw_path) + if not path.is_absolute(): + path = skill_dir / path + return path.resolve() + + +def should_scan_claim_surface(path: Path) -> bool: + parts = path.parts + if "release_snapshots" in parts: + return False + if path.name in {"world_class_claim_guard.md", "world_class_claim_guard.html"}: + return False + if "dist" in parts and "install-simulation" in parts: + return False + if "evidence" in parts and "world_class" in parts and "submissions" in parts: + return False + return True + + +def default_claim_surfaces(skill_dir: Path) -> list[Path]: + paths: list[Path] = [] + for rel in ["README.md", "SKILL.md", "manifest.json"]: + path = skill_dir / rel + if path.exists(): + paths.append(path) + for directory, patterns in [ + (skill_dir / "agents", ["*.yaml", "*.yml", "*.json"]), + (skill_dir / "docs", ["*.md"]), + (skill_dir / "evidence" / "world_class", ["*.md", "*.json"]), + (skill_dir / "dist", ["*.json", "*.md"]), + (skill_dir / "reports", ["*.md", "*.html", "*.json"]), + (skill_dir / "registry", ["*.json"]), + (skill_dir / "security", ["*.md", "*.json"]), + (skill_dir / "skill-ir", ["*.json"]), + ]: + if not directory.exists(): + continue + for pattern in patterns: + paths.extend( + path + for path in directory.rglob(pattern) + if path.is_file() and should_scan_claim_surface(path) + ) + return sorted(set(paths), key=lambda item: rel_path(item, skill_dir)) + + +def line_matches(path: Path, ledger_ready: bool) -> list[dict[str, Any]]: + if ledger_ready: + return [] + violations: list[dict[str, Any]] = [] + try: + lines = path.read_text(encoding="utf-8", errors="replace").splitlines() + except OSError: + return violations + for lineno, line in enumerate(lines, start=1): + for rule in FORBIDDEN_WHEN_PENDING: + if rule["pattern"].search(line): + violations.append( + { + "line": lineno, + "rule": rule["key"], + "reason": rule["reason"], + "excerpt": line.strip()[:240], + } + ) + return violations + + +def escape_markdown_table_cell(value: Any) -> str: + return str(value).replace("|", "\\|") + + +def build_guard(skill_dir: Path, generated_at: str, claim_surfaces: list[Path] | None = None) -> dict[str, Any]: + ledger = build_ledger(skill_dir, generated_at) + ledger_ready = ledger.get("summary", {}).get("ready_to_claim_world_class") is True + surfaces = claim_surfaces or default_claim_surfaces(skill_dir) + scanned = [] + violations = [] + for path in surfaces: + matches = line_matches(path, ledger_ready) + rel = rel_path(path, skill_dir) + scanned.append({"path": rel, "violation_count": len(matches)}) + for match in matches: + violations.append({"path": rel, **match}) + ok = len(violations) == 0 + json_surface_count = sum(1 for item in scanned if item["path"].endswith(".json")) + metadata_surface_count = sum( + 1 for item in scanned if item["path"].endswith((".json", ".yaml", ".yml")) + ) + package_surface_count = sum( + 1 + for item in scanned + if item["path"] == "manifest.json" + or item["path"].startswith(("agents/", "dist/", "security/")) + ) + return { + "schema_version": "1.0", + "ok": ok, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": { + "ledger_ready_to_claim_world_class": ledger_ready, + "ledger_pending_count": ledger.get("summary", {}).get("pending_count", 0), + "claim_surface_count": len(scanned), + "json_claim_surface_count": json_surface_count, + "metadata_claim_surface_count": metadata_surface_count, + "package_claim_surface_count": package_surface_count, + "violation_count": len(violations), + "overclaim_guard_active": True, + "decision": ( + "claim-ready" + if ledger_ready and ok + else ("claim-blocked-overclaim" if violations else "claim-guard-pass-evidence-pending") + ), + }, + "rules": [ + {"key": item["key"], "reason": item["reason"], "pattern": item["pattern"].pattern} + for item in FORBIDDEN_WHEN_PENDING + ], + "scanned_surfaces": scanned, + "violations": violations, + "source_ledger": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md", + }, + "artifacts": { + "json": "reports/world_class_claim_guard.json", + "markdown": "reports/world_class_claim_guard.md", + }, + } + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# World-Class Claim Guard", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- ledger ready to claim world-class: `{str(summary['ledger_ready_to_claim_world_class']).lower()}`", + f"- ledger pending evidence: `{summary['ledger_pending_count']}`", + f"- claim surfaces scanned: `{summary['claim_surface_count']}`", + f"- JSON claim surfaces scanned: `{summary['json_claim_surface_count']}`", + f"- metadata claim surfaces scanned: `{summary['metadata_claim_surface_count']}`", + f"- package/runtime claim surfaces scanned: `{summary['package_claim_surface_count']}`", + f"- violations: `{summary['violation_count']}`", + f"- overclaim guard active: `{str(summary['overclaim_guard_active']).lower()}`", + "", + "This guard scans public claim surfaces, machine-readable reports, and package/runtime metadata for completion language that would contradict the world-class evidence ledger. It allows evidence planning and pending-state language, but blocks completion claims until the ledger is ready.", + "", + "## Violations", + "", + "| Path | Line | Rule | Excerpt |", + "| --- | ---: | --- | --- |", + ] + if report["violations"]: + for item in report["violations"]: + excerpt = str(item["excerpt"]).replace("|", "\\|") + lines.append(f"| `{item['path']}` | {item['line']} | `{item['rule']}` | {excerpt} |") + else: + lines.append("| `none` | 0 | `none` | none |") + lines.extend( + [ + "", + "## Rules", + "", + "| Rule | Reason |", + "| --- | --- |", + ] + ) + for rule in report["rules"]: + reason = escape_markdown_table_cell(rule["reason"]) + lines.append(f"| `{rule['key']}` | {reason} |") + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Guard public world-class completion claims against the evidence ledger.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--claim-surface", action="append", default=[]) + parser.add_argument("--output-json", default="reports/world_class_claim_guard.json") + parser.add_argument("--output-md", default="reports/world_class_claim_guard.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + claim_surfaces = [resolve_claim_surface(item, skill_dir) for item in args.claim_surface] if args.claim_surface else None + report = build_guard(skill_dir, args.generated_at, claim_surfaces) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_world_class_evidence_intake.py b/scripts/render_world_class_evidence_intake.py new file mode 100644 index 0000000..328c3b9 --- /dev/null +++ b/scripts/render_world_class_evidence_intake.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python3 +import argparse +import json +import shlex +from datetime import date +from pathlib import Path +from typing import Any + +from render_world_class_evidence_ledger import build_ledger +from world_class_evidence_contract import load_json, rel_path, validate_payload + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Validates world-class human and external evidence intake packets before ledger review." + +def template_paths(skill_dir: Path, keys: list[str]) -> dict[str, Path]: + template_dir = skill_dir / "evidence" / "world_class" / "templates" + return {key: template_dir / f"{key}.intake.json" for key in keys} + + +def submission_paths(submissions_dir: Path) -> list[Path]: + if not submissions_dir.exists(): + return [] + ignored_names = {"submission_manifest.json"} + return sorted(path for path in submissions_dir.glob("*.json") if path.is_file() and path.name not in ignored_names) + + +def find_entry(entries: list[dict[str, Any]], key: str) -> dict[str, Any] | None: + for entry in entries: + if entry.get("key") == key: + return entry + return None + + +def first_by_key(items: list[dict[str, Any]]) -> dict[str, dict[str, Any]]: + by_key: dict[str, dict[str, Any]] = {} + for item in items: + key = str(item.get("evidence_key", "")) + if key and key not in by_key: + by_key[key] = item + return by_key + + +def shell_path(path: Path, root: Path) -> str: + return shlex.quote(rel_path(path, root)) + + +def checklist_readiness( + entry: dict[str, Any], + template_result: dict[str, Any] | None, + submission_result: dict[str, Any] | None, +) -> tuple[str, str]: + if entry.get("status") == "accepted": + return "accepted", "Ledger already accepts this evidence item." + if template_result is None or template_result.get("status") != "pass": + return "fix-template", "The intake template is missing or invalid." + if submission_result is None: + return "awaiting-submission", "No real evidence submission has been provided yet." + if submission_result.get("status") == "pass": + observed = entry.get("observed_state", {}) if isinstance(entry.get("observed_state", {}), dict) else {} + if entry.get("source_accepted") is True or observed.get("accepted") is True: + return "ready-for-ledger-review", "Submission passes intake validation and is ready for ledger review." + return "source-evidence-incomplete", "Submission passes intake validation, but the source evidence checks still do not pass." + return "fix-submission", "Submission exists but failed intake validation." + + +def build_operator_checklist( + skill_dir: Path, + ledger: dict[str, Any], + template_results: list[dict[str, Any]], + submission_results: list[dict[str, Any]], + submissions_dir: Path, +) -> list[dict[str, Any]]: + templates_by_key = first_by_key(template_results) + submissions_by_key = first_by_key(submission_results) + checklist = [] + for entry in ledger.get("entries", []): + key = str(entry.get("key", "")) + template_path = skill_dir / "evidence" / "world_class" / "templates" / f"{key}.intake.json" + submission_path = submissions_dir / f"{key}.json" + template_result = templates_by_key.get(key) + submission_result = submissions_by_key.get(key) + readiness, blocking_reason = checklist_readiness(entry, template_result, submission_result) + submissions_dir_arg = shell_path(submissions_dir, skill_dir) + checklist.append( + { + "evidence_key": key, + "label": entry.get("label", key), + "category": entry.get("category", "external"), + "owner": entry.get("owner", "release reviewer"), + "readiness": readiness, + "blocking_reason": blocking_reason, + "template_status": template_result.get("status", "missing") if template_result else "missing", + "submission_status": submission_result.get("status", "missing") if submission_result else "missing", + "source_accepted": entry.get("source_accepted") is True, + "observed_state": entry.get("observed_state", {}), + "template_path": rel_path(template_path, skill_dir), + "submission_path": rel_path(submission_path, skill_dir), + "commands": { + "prepare_submission": ( + "python3 scripts/yao.py world-class-submission-kit . " + f"--evidence-key {shlex.quote(key)} --output-dir {shell_path(submission_path.parent, skill_dir)}" + ), + "validate_intake": f"python3 scripts/yao.py world-class-intake . --submissions-dir {submissions_dir_arg}", + "submission_review": ( + f"python3 scripts/yao.py world-class-submission-review . --submissions-dir {submissions_dir_arg}" + ), + "refresh_ledger": f"python3 scripts/yao.py world-class-ledger . --submissions-dir {submissions_dir_arg}", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard .", + }, + "must_collect": { + "provenance_requirements": entry.get("provenance_requirements", []), + "runbook": entry.get("runbook", []), + "success_checks": entry.get("success_checks", []), + "evidence_artifacts": entry.get("evidence_artifacts", []), + "privacy_contract": entry.get("privacy_contract", []), + }, + "anti_overclaim": entry.get("anti_overclaim", {}), + "next_action": entry.get("next_action", ""), + } + ) + return checklist + + +def build_intake(skill_dir: Path, generated_at: str, submissions_dir: Path | None = None) -> dict[str, Any]: + ledger = build_ledger(skill_dir, generated_at) + entries = ledger.get("entries", []) + keys = [str(entry.get("key", "")) for entry in entries] + schema_path = skill_dir / "evidence" / "world_class" / "intake.schema.json" + template_results = [] + for key, path in template_paths(skill_dir, keys).items(): + entry = find_entry(entries, key) or {"key": key, "category": "external"} + payload = load_json(path) + if not payload: + template_results.append( + { + "path": rel_path(path, skill_dir), + "evidence_key": key, + "status": "fail", + "template_only": True, + "errors": ["template missing or invalid JSON"], + } + ) + continue + template_results.append(validate_payload(payload, entry, path=path, root=skill_dir, template_expected=True)) + + submissions_dir = submissions_dir or (skill_dir / "evidence" / "world_class" / "submissions") + submission_results = [] + for path in submission_paths(submissions_dir): + payload = load_json(path) + key = str(payload.get("evidence_key", "")) + entry = find_entry(entries, key) + if not payload or entry is None: + submission_results.append( + { + "path": rel_path(path, skill_dir), + "evidence_key": key or "unknown", + "status": "fail", + "template_only": False, + "errors": ["submission missing, invalid JSON, or unknown evidence_key"], + } + ) + continue + submission_results.append(validate_payload(payload, entry, path=path, root=skill_dir, template_expected=False)) + + template_pass_count = sum(1 for item in template_results if item["status"] == "pass") + valid_submission_count = sum(1 for item in submission_results if item["status"] == "pass") + invalid_submission_count = sum(1 for item in submission_results if item["status"] == "fail") + schema_exists = schema_path.exists() + intake_ready = schema_exists and template_pass_count == len(keys) and invalid_submission_count == 0 + operator_checklist = build_operator_checklist(skill_dir, ledger, template_results, submission_results, submissions_dir) + ready_checklist_count = sum(1 for item in operator_checklist if item["readiness"] in {"accepted", "ready-for-ledger-review"}) + source_incomplete_count = sum(1 for item in operator_checklist if item["readiness"] == "source-evidence-incomplete") + return { + "schema_version": "1.0", + "ok": intake_ready, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": { + "schema_present": schema_exists, + "ledger_entry_count": len(keys), + "template_count": len(template_results), + "template_pass_count": template_pass_count, + "submission_count": len(submission_results), + "valid_submission_count": valid_submission_count, + "invalid_submission_count": invalid_submission_count, + "operator_checklist_count": len(operator_checklist), + "operator_checklist_ready_count": ready_checklist_count, + "valid_packet_source_incomplete_count": source_incomplete_count, + "ready_for_external_collection": intake_ready, + "ready_for_ledger_review": ready_checklist_count > 0, + "ready_to_claim_world_class": ledger.get("summary", {}).get("ready_to_claim_world_class") is True, + "overclaim_guard_active": True, + "decision": ( + "fix-intake" + if not intake_ready + else ( + "intake-ready-for-ledger-review" + if ready_checklist_count + else ("source-evidence-incomplete" if source_incomplete_count else "awaiting-submissions") + ) + ), + }, + "schema": rel_path(schema_path, skill_dir), + "templates": template_results, + "submissions": submission_results, + "operator_checklist": operator_checklist, + "source_ledger": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md", + "pending_count": ledger.get("summary", {}).get("pending_count", 0), + }, + "artifacts": { + "json": "reports/world_class_evidence_intake.json", + "markdown": "reports/world_class_evidence_intake.md", + }, + } + + +def render_table(items: list[dict[str, Any]]) -> list[str]: + lines = ["| Evidence | Status | Path | Artifacts | Errors |", "| --- | --- | --- | --- | --- |"] + if not items: + lines.append("| `none` | `n/a` | none | none | none |") + return lines + for item in items: + errors = "; ".join(item.get("errors", [])) or "none" + safe_errors = errors.replace("|", "\\|") + integrity = item.get("artifact_integrity", {}) if isinstance(item.get("artifact_integrity", {}), dict) else {} + artifact_summary = ( + f"{integrity.get('artifact_existing_count', 0)} existing / " + f"{integrity.get('artifact_sha256_verified_count', 0)} sha256 verified / " + f"{integrity.get('required_artifact_verified_count', 0)} required verified / " + f"{integrity.get('artifact_ref_count', 0)} refs" + ) + lines.append( + f"| `{item['evidence_key']}` | `{item['status']}` | `{item['path']}` | {artifact_summary} | {safe_errors} |" + ) + return lines + + +def render_operator_checklist(items: list[dict[str, Any]]) -> list[str]: + lines = [ + "| Evidence | Readiness | Submission | Next action |", + "| --- | --- | --- | --- |", + ] + if not items: + lines.append("| `none` | `accepted` | none | none |") + return lines + for item in items: + action = str(item.get("next_action", "")).replace("|", "\\|") + lines.append( + f"| `{item['evidence_key']}` | `{item['readiness']}` | `{item['submission_status']}` | {action} |" + ) + for item in items: + lines.extend(["", f"### {item['label']}", ""]) + lines.append(f"- readiness: `{item['readiness']}`") + lines.append(f"- blocking reason: {item['blocking_reason']}") + lines.append(f"- owner: {item['owner']}") + lines.append(f"- template: `{item['template_path']}`") + lines.append(f"- submission: `{item['submission_path']}`") + lines.extend(["", "#### Commands", ""]) + commands = item.get("commands", {}) + for label in ["prepare_submission", "validate_intake", "submission_review", "refresh_ledger", "guard_claim"]: + if commands.get(label): + lines.append(f"- {label}: `{commands[label]}`") + must_collect = item.get("must_collect", {}) + lines.extend(["", "#### Must Collect", ""]) + for label in ["provenance_requirements", "success_checks", "evidence_artifacts", "privacy_contract"]: + values = must_collect.get(label, []) + if values: + lines.append(f"- {label}:") + lines.extend(f" - {value}" for value in values) + runbook = must_collect.get("runbook", []) + if runbook: + lines.extend(["", "#### Source Runbook", ""]) + lines.extend(f"- `{value}`" if str(value).startswith("python3 ") or "=" in str(value) else f"- {value}" for value in runbook) + return lines + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# World-Class Evidence Intake", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- schema present: `{str(summary['schema_present']).lower()}`", + f"- templates: `{summary['template_pass_count']}` / `{summary['template_count']}`", + f"- submissions: `{summary['valid_submission_count']}` valid / `{summary['submission_count']}` total", + f"- invalid submissions: `{summary['invalid_submission_count']}`", + f"- valid packet but source incomplete: `{summary['valid_packet_source_incomplete_count']}`", + f"- operator checklist: `{summary['operator_checklist_ready_count']}` ready / `{summary['operator_checklist_count']}` total", + f"- ready for external collection: `{str(summary['ready_for_external_collection']).lower()}`", + f"- ready for ledger review: `{str(summary['ready_for_ledger_review']).lower()}`", + f"- ready to claim world-class: `{str(summary['ready_to_claim_world_class']).lower()}`", + f"- overclaim guard active: `{str(summary['overclaim_guard_active']).lower()}`", + "", + "This report validates the intake contract for human and external evidence. A valid intake packet means the evidence is ready for ledger review; it does not by itself make a world-class claim true.", + "", + "## Templates", + "", + *render_table(report["templates"]), + "", + "## Submissions", + "", + *render_table(report["submissions"]), + "", + "## Operator Checklist", + "", + *render_operator_checklist(report["operator_checklist"]), + "", + "## Boundary", + "", + "- Templates and planned work do not count as accepted evidence.", + "- Real submissions must include the evidence-key critical artifact paths with verified SHA-256 digests.", + "- Real submissions must replace template submitter, date, and provenance placeholders with concrete evidence metadata.", + "- Local command-runner output does not count as provider-backed model evidence.", + "- Metadata fallback does not count as native permission enforcement.", + "- Pending reviewer work does not count as human adjudication.", + ] + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Validate world-class evidence intake packets.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--submissions-dir") + parser.add_argument("--output-json", default="reports/world_class_evidence_intake.json") + parser.add_argument("--output-md", default="reports/world_class_evidence_intake.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + submissions_dir = Path(args.submissions_dir).resolve() if args.submissions_dir else None + report = build_intake(skill_dir, args.generated_at, submissions_dir) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_world_class_evidence_ledger.py b/scripts/render_world_class_evidence_ledger.py new file mode 100644 index 0000000..18911c5 --- /dev/null +++ b/scripts/render_world_class_evidence_ledger.py @@ -0,0 +1,389 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + +from output_review_privacy import forbidden_raw_content_field_paths +from render_world_class_evidence_plan import build_plan +from world_class_evidence_contract import load_json, load_json_with_status, rel_path, validate_payload +from world_class_source_checks import build_source_checklist, summarize_source_checklist + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders a machine-checkable ledger for world-class external and human evidence gaps." + + +def provider_state(skill_dir: Path) -> dict[str, Any]: + summary = load_json(skill_dir / "reports" / "output_execution_runs.json").get("summary", {}) + return { + "model_executed_count": summary.get("model_executed_count", 0), + "timing_observed_count": summary.get("timing_observed_count", 0), + "token_observed_count": summary.get("token_observed_count", 0), + "accepted": ( + summary.get("model_executed_count", 0) > 0 + and summary.get("timing_observed_count", 0) > 0 + and summary.get("token_observed_count", 0) > 0 + ), + } + + +def human_state(skill_dir: Path) -> dict[str, Any]: + adjudication = load_json(skill_dir / "reports" / "output_review_adjudication.json") + summary = adjudication.get("summary", {}) + raw_content_paths = forbidden_raw_content_field_paths(adjudication, "output_review_adjudication") + return { + "pair_count": summary.get("pair_count", 0), + "judgment_count": summary.get("judgment_count", 0), + "pending_count": summary.get("pending_count", 0), + "invalid_decision_count": summary.get("invalid_decision_count", 0), + "answer_revealed_count": summary.get("answer_revealed_count", 0), + "reviewer_metadata_present": summary.get("reviewer_metadata_present", False), + "reason_required": summary.get("reason_required", False), + "blind_review_attested": summary.get("blind_review_attested", False), + "raw_content_excluded_attested": summary.get("raw_content_excluded_attested", False), + "reviewer_reason_required_attested": summary.get("reviewer_reason_required_attested", False), + "ready_for_human_evidence": summary.get("ready_for_human_evidence", False), + "raw_content_allowed": bool(raw_content_paths), + "raw_content_path_count": len(raw_content_paths), + "accepted": ( + summary.get("ready_for_human_evidence") is True + and not raw_content_paths + ), + } + + +def permission_state(skill_dir: Path) -> dict[str, Any]: + summary = load_json(skill_dir / "reports" / "runtime_permission_probes.json").get("summary", {}) + return { + "native_enforcement_count": summary.get("native_enforcement_count", 0), + "metadata_fallback_count": summary.get("metadata_fallback_count", 0), + "installer_enforcement_pass_count": summary.get("installer_enforcement_pass_count", 0), + "installer_permission_failure_count": summary.get("installer_permission_failure_count", 0), + "installer_enforcement_ready": summary.get("installer_enforcement_ready", False), + "residual_risk_count": summary.get("residual_risk_count", 0), + "failure_count": summary.get("failure_count", 0), + "accepted": summary.get("native_enforcement_count", 0) > 0 and summary.get("failure_count", 0) == 0, + } + + +def telemetry_state(skill_dir: Path) -> dict[str, Any]: + payload = load_json(skill_dir / "reports" / "adoption_drift_report.json") + summary = payload.get("summary", {}) + source_types = summary.get("source_types", {}) if isinstance(summary.get("source_types", {}), dict) else {} + privacy = payload.get("privacy_contract", {}) + return { + "external_source_events": source_types.get("external", 0), + "adoption_sample_count": summary.get("adoption_sample_count", 0), + "raw_content_allowed": privacy.get("raw_content_allowed"), + "risk_band": summary.get("risk_band", "missing"), + "accepted": ( + source_types.get("external", 0) > 0 + and summary.get("adoption_sample_count", 0) > 0 + and privacy.get("raw_content_allowed") is False + ), + } + + +STATE_LOADERS = { + "provider-holdout": provider_state, + "human-adjudication": human_state, + "native-permission-enforcement": permission_state, + "native-client-telemetry": telemetry_state, +} + +PROVENANCE_REQUIREMENTS = { + "provider-holdout": ["provider-backed model run", "observed timing", "observed token metadata"], + "human-adjudication": ["real reviewer identity", "blind A/B decisions", "answer key unopened until decisions exist"], + "native-permission-enforcement": ["real target client or external installer runtime guard", "native enforcement flag or externally accepted guard proof", "residual risk retained for fallback targets"], + "native-client-telemetry": ["real external client source", "metadata-only event", "local-first import path"], +} + +TOP_LEVEL_SUMMARY_FIELDS = [ + "decision", + "ready_to_claim_world_class", + "ledger_entry_count", + "source_accepted_count", + "accepted_count", + "pending_count", + "human_pending_count", + "external_pending_count", + "submitted_entry_count", + "reviewer_approved_submission_count", + "missing_submission_count", + "invalid_submission_count", + "source_check_count", + "source_pass_count", + "source_blocked_count", + "submitted_but_pending_count", + "source_accepted_without_valid_submission_count", + "overclaim_guard_active", +] + + +def top_level_summary_mirrors(summary: dict[str, Any]) -> dict[str, Any]: + return {key: summary[key] for key in TOP_LEVEL_SUMMARY_FIELDS if key in summary} + + +def submission_state(skill_dir: Path, task: dict[str, Any], submissions_dir: Path) -> dict[str, Any]: + key = str(task.get("key", "")) + path = submissions_dir / f"{key}.json" + payload, load_status = load_json_with_status(path) + if load_status != "present": + return { + "status": load_status, + "path": rel_path(path, skill_dir), + "artifact_ref_count": 0, + "attested_real_evidence": False, + "privacy_contract_satisfied": False, + "ledger_reviewer_approved": False, + "ledger_reviewer": "", + "ledger_reviewed_at": "", + "ledger_counts_as_completion": False, + } + validation = validate_payload(payload, task, path=path, root=skill_dir, template_expected=False) + refs = payload.get("artifact_refs", []) + attestation = payload.get("attestation", {}) if isinstance(payload.get("attestation", {}), dict) else {} + status = "submitted" if validation["status"] == "pass" else "invalid-contract" + artifact_integrity = validation.get("artifact_integrity", {}) + return { + "status": status, + "path": rel_path(path, skill_dir), + "submitted_by": payload.get("submitted_by", ""), + "submitted_at": payload.get("submitted_at", ""), + "artifact_ref_count": len(refs) if isinstance(refs, list) else 0, + "artifact_existing_count": artifact_integrity.get("artifact_existing_count", 0), + "artifact_sha256_verified_count": artifact_integrity.get("artifact_sha256_verified_count", 0), + "attested_real_evidence": attestation.get("real_external_or_human_evidence") is True, + "reviewer_or_operator_identity_present": attestation.get("reviewer_or_operator_identity_present") is True, + "privacy_contract_satisfied": attestation.get("privacy_contract_satisfied") is True, + "ledger_reviewer_approved": attestation.get("ledger_reviewer_approved") is True, + "ledger_reviewer": str(attestation.get("ledger_reviewer", "")).strip(), + "ledger_reviewed_at": str(attestation.get("ledger_reviewed_at", "")).strip(), + "errors": validation.get("errors", []), + "ledger_counts_as_completion": False, + } + + +def build_entry(skill_dir: Path, task: dict[str, Any], submissions_dir: Path) -> dict[str, Any]: + state = STATE_LOADERS.get(task["key"], lambda _: {"accepted": task["status"] == "pass"})(skill_dir) + submission = submission_state(skill_dir, task, submissions_dir) + source_checklist = build_source_checklist([{"key": task["key"], "observed_state": state}]) + source_summary = summarize_source_checklist(source_checklist) + source_accepted = bool(source_checklist) and source_summary["source_blocked_count"] == 0 + accepted = source_accepted and submission.get("status") == "submitted" and submission.get("ledger_reviewer_approved") is True + return { + "key": task["key"], + "label": task["label"], + "category": task["category"], + "owner": task["owner"], + "status": "accepted" if accepted else "pending", + "source_status": task["status"], + "source_accepted": source_accepted, + "current": task["current"], + "objective": task["objective"], + "runbook": task.get("runbook", []), + "provenance_requirements": PROVENANCE_REQUIREMENTS.get(task["key"], ["release reviewer evidence"]), + "success_checks": task["success_checks"], + "evidence_artifacts": task["evidence_artifacts"], + "privacy_contract": task["privacy_contract"], + "observed_state": state, + "source_checklist": source_checklist, + **source_summary, + "submission_state": submission, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "next_action": task["audit_next_action"], + } + + +def build_ledger(skill_dir: Path, generated_at: str, submissions_dir: Path | None = None) -> dict[str, Any]: + plan = build_plan(skill_dir, generated_at) + submissions_dir = submissions_dir or (skill_dir / "evidence" / "world_class" / "submissions") + evidence_requirements = plan.get("evidence_requirements") or plan.get("tasks", []) + entries = [build_entry(skill_dir, task, submissions_dir) for task in evidence_requirements] + source_rows = [row for entry in entries for row in entry.get("source_checklist", [])] + source_summary = summarize_source_checklist(source_rows) + source_accepted_count = sum(1 for entry in entries if entry.get("source_accepted") is True) + accepted_count = sum(1 for entry in entries if entry["status"] == "accepted") + pending_count = len(entries) - accepted_count + human_pending_count = sum(1 for entry in entries if entry["category"] == "human" and entry["status"] == "pending") + external_pending_count = sum(1 for entry in entries if entry["category"] == "external" and entry["status"] == "pending") + submitted_entry_count = sum(1 for entry in entries if entry["submission_state"]["status"] == "submitted") + reviewer_approved_submission_count = sum( + 1 for entry in entries if entry["submission_state"].get("ledger_reviewer_approved") is True + ) + missing_submission_count = sum(1 for entry in entries if entry["submission_state"]["status"] == "missing") + invalid_submission_count = sum(1 for entry in entries if entry["submission_state"]["status"] in {"invalid-json", "invalid-contract"}) + submitted_but_pending_count = sum( + 1 for entry in entries if entry["submission_state"]["status"] == "submitted" and entry["status"] == "pending" + ) + source_accepted_without_valid_submission_count = sum( + 1 + for entry in entries + if entry.get("source_accepted") is True and entry["submission_state"]["status"] != "submitted" + ) + ready = bool(entries) and pending_count == 0 and plan["summary"].get("world_class_ready") is True + summary = { + "ledger_entry_count": len(entries), + "source_accepted_count": source_accepted_count, + "accepted_count": accepted_count, + "pending_count": pending_count, + "human_pending_count": human_pending_count, + "external_pending_count": external_pending_count, + "submitted_entry_count": submitted_entry_count, + "reviewer_approved_submission_count": reviewer_approved_submission_count, + "missing_submission_count": missing_submission_count, + "invalid_submission_count": invalid_submission_count, + **source_summary, + "submitted_but_pending_count": submitted_but_pending_count, + "source_accepted_without_valid_submission_count": source_accepted_without_valid_submission_count, + "overclaim_guard_active": True, + "ready_to_claim_world_class": ready, + "decision": "ready-for-completion-audit" if ready else "evidence-pending", + } + return { + "schema_version": "1.0", + "ok": True, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + **top_level_summary_mirrors(summary), + "summary": summary, + "report_contract": { + "schema_version": "1.0", + "contract": "world-class-evidence-ledger", + "top_level_mirrors_summary": True, + "summary_fields": TOP_LEVEL_SUMMARY_FIELDS, + "source_of_truth": "summary", + }, + "entries": entries, + "source_plan": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "task_count": plan["summary"].get("task_count", 0), + "evidence_requirement_count": len(evidence_requirements), + }, + "submissions": { + "directory": rel_path(submissions_dir, skill_dir), + "ledger_counts_submission_as_completion": False, + "source_pass_requires_valid_submission": True, + }, + "artifacts": { + "json": "reports/world_class_evidence_ledger.json", + "markdown": "reports/world_class_evidence_ledger.md", + "intake": "reports/world_class_evidence_intake.md", + }, + } + + +def render_markdown(ledger: dict[str, Any]) -> str: + summary = ledger["summary"] + lines = [ + "# World-Class Evidence Ledger", + "", + f"Generated at: `{ledger['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- ready to claim world-class: `{str(summary['ready_to_claim_world_class']).lower()}`", + f"- entries: `{summary['ledger_entry_count']}`", + f"- source accepted: `{summary.get('source_accepted_count', 0)}`", + f"- source checks: `{summary.get('source_pass_count', 0)}` pass / `{summary.get('source_check_count', 0)}` total", + f"- source blocked: `{summary.get('source_blocked_count', 0)}`", + f"- accepted: `{summary['accepted_count']}`", + f"- pending: `{summary['pending_count']}`", + f"- human pending: `{summary['human_pending_count']}`", + f"- external pending: `{summary['external_pending_count']}`", + f"- submitted entries: `{summary['submitted_entry_count']}`", + f"- reviewer approved submissions: `{summary.get('reviewer_approved_submission_count', 0)}`", + f"- submitted but pending: `{summary['submitted_but_pending_count']}`", + f"- source accepted without valid submission: `{summary.get('source_accepted_without_valid_submission_count', 0)}`", + f"- invalid submissions: `{summary['invalid_submission_count']}`", + f"- overclaim guard active: `{str(summary['overclaim_guard_active']).lower()}`", + "", + "This ledger records the current evidence state. It requires both passing source evidence and a validated intake submission with artifact SHA-256 checks before accepting an item. It does not treat planned work, metadata fallback, pending review, or local command-runner output as world-class completion evidence.", + "", + "## Ledger", + "", + "| Evidence | Status | Submission | Category | Current | Next action |", + "| --- | --- | --- | --- | --- | --- |", + ] + for entry in ledger["entries"]: + current = str(entry["current"]).replace("|", "\\|") + action = str(entry["next_action"]).replace("|", "\\|") + submission = entry.get("submission_state", {}).get("status", "missing") + lines.append(f"| `{entry['key']}` | `{entry['status']}` | `{submission}` | `{entry['category']}` | {current} | {action} |") + if not ledger["entries"]: + lines.append("| `none` | `accepted` | `none` | all evidence collected | none |") + for entry in ledger["entries"]: + lines.extend(["", f"## {entry['label']}", ""]) + lines.append(f"- objective: {entry['objective']}") + lines.append(f"- source status: `{entry['source_status']}`") + lines.append(f"- observed state: `{json.dumps(entry['observed_state'], ensure_ascii=False)}`") + lines.append( + f"- source checks: `{entry.get('source_pass_count', 0)}` pass / " + f"`{entry.get('source_check_count', 0)}` total" + ) + lines.append(f"- submission state: `{json.dumps(entry.get('submission_state', {}), ensure_ascii=False)}`") + lines.extend(["", "### Provenance Requirements", ""]) + lines.extend(f"- {item}" for item in entry["provenance_requirements"]) + lines.extend(["", "### Source Runbook", ""]) + lines.extend(f"- `{item}`" if str(item).startswith("python3 ") or "=" in str(item) else f"- {item}" for item in entry.get("runbook", [])) + lines.extend( + [ + "", + "### Source Evidence Checks", + "", + "| Check | Current | Expected | Status |", + "| --- | --- | --- | --- |", + ] + ) + source_checks = entry.get("source_checklist", []) + if source_checks: + for row in source_checks: + lines.append( + f"| {row['label']} | `{row['actual']}` | `{row['expected']}` | `{row['status']}` |" + ) + else: + lines.append("| No source checks listed. | `n/a` | `n/a` | `n/a` |") + lines.extend(["", "### Completion Assertions", ""]) + lines.extend(f"- {item}" for item in entry["success_checks"]) + lines.extend(["", "### Privacy Contract", ""]) + lines.extend(f"- {item}" for item in entry["privacy_contract"]) + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a world-class evidence ledger.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json", default="reports/world_class_evidence_ledger.json") + parser.add_argument("--output-md", default="reports/world_class_evidence_ledger.md") + parser.add_argument("--submissions-dir") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + submissions_dir = Path(args.submissions_dir).resolve() if args.submissions_dir else None + ledger = build_ledger(skill_dir, args.generated_at, submissions_dir=submissions_dir) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(ledger, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(ledger), encoding="utf-8") + print(json.dumps(ledger, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_world_class_evidence_plan.py b/scripts/render_world_class_evidence_plan.py new file mode 100644 index 0000000..30ebc62 --- /dev/null +++ b/scripts/render_world_class_evidence_plan.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + +from render_skill_os2_audit import build_audit +from world_class_evidence_contract import load_json_with_status, validate_payload + + +ROOT = Path(__file__).resolve().parent.parent + + +TASK_TEMPLATES: dict[str, dict[str, Any]] = { + "provider-holdout": { + "category": "external", + "owner": "operator with provider credentials", + "objective": "Collect at least one provider-backed output-eval holdout run with model, timing, and token metadata.", + "runbook": [ + "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + "For OpenAI Responses: python3 scripts/yao.py output-exec --provider-runner openai --provider-model ${YAO_OUTPUT_EVAL_MODEL:-gpt-4.1-mini} --timeout-seconds 60", + "For DeepSeek Chat Completions: python3 scripts/yao.py output-exec --provider-runner deepseek --provider-model deepseek-v4-flash --provider-api-format chat-completions --provider-thinking disabled --api-key-env DEEPSEEK_API_KEY --timeout-seconds 120", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + ], + "success_checks": [ + "reports/output_execution_runs.json summary.model_executed_count > 0", + "reports/output_execution_runs.json summary.timing_observed_count > 0", + "reports/output_execution_runs.json summary.token_observed_count > 0", + "reports/skill_os2_audit.json item provider-holdout status becomes pass", + ], + "evidence_artifacts": [ + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/skill_os2_audit.json", + ], + "privacy_contract": [ + "Do not commit provider credentials or environment dumps.", + "The output execution report records output hashes and aggregate run metadata, not raw provider prompts.", + ], + }, + "human-adjudication": { + "category": "human", + "owner": "human reviewer", + "objective": "Record real blind A/B reviewer decisions before claiming human output review completion.", + "runbook": [ + "python3 scripts/yao.py output-review-kit --write-template", + "Open reports/output_review_kit.md and choose A or B for each pair without opening the answer key.", + "python3 scripts/adjudicate_output_review.py --write-template", + "Record reviewer choices in a separate JSON, JSONL, or CSV decision source with reviewer, reviewed_at, case_id, winner_variant, confidence, required reason, and truthful reviewer_attestation only.", + "python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication", + "python3 scripts/yao.py output-review", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + ], + "success_checks": [ + "reports/output_review_adjudication.json summary.pending_count == 0", + "reports/output_review_adjudication.json summary.judgment_count == summary.pair_count", + "reports/output_review_adjudication.json summary.invalid_decision_count == 0", + "reports/output_review_adjudication.json summary.reviewer_metadata_present is true", + "reports/output_review_adjudication.json summary.blind_review_attested is true", + "reports/output_review_adjudication.json review_integrity.blind_pack_sha256 exists and matches reports/output_review_decisions.json", + "reports/output_review_adjudication.json pairs and reviewer_checklist store prompt_sha256, not raw prompt text", + "reports/output_review_adjudication.json summary.ready_for_human_evidence is true", + "reports/skill_os2_audit.json item human-adjudication status becomes pass", + ], + "evidence_artifacts": [ + "reports/output_blind_review_pack.md", + "reports/output_review_kit.md", + "reports/output_review_decisions.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "scripts/import_output_review_decisions.py", + ], + "privacy_contract": [ + "Reviewer decisions should not include raw user data or private customer detail.", + "Reviewer reasons must be rubric-based and must not include raw user data or private customer detail.", + "The decision importer rejects raw prompt, output, transcript, message, and answer-key fields.", + "The adjudication evidence stores prompt_sha256 instead of raw prompt text.", + "The decision and adjudication artifacts preserve blind_pack_sha256 so reviewers can audit exactly which pack was judged.", + "Keep the answer key separate until after decisions are recorded.", + ], + }, + "native-permission-enforcement": { + "category": "external", + "owner": "target client or installer integrator", + "objective": "Prove at least one real target client or external installer runtime guard enforces approved high-permission capabilities.", + "runbook": [ + "Implement or connect a real target client or external installer runtime guard that blocks undeclared network, file_write, or subprocess capabilities.", + "Update the generated target adapter only when the guard is actually enforced by that target.", + "python3 scripts/yao.py package . --platform openai --platform claude --platform generic --platform vscode --output-dir dist --zip", + "python3 scripts/yao.py install-simulate . --package-dir dist --install-root dist/install-simulation", + "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + ], + "success_checks": [ + "reports/runtime_permission_probes.json summary.native_enforcement_count > 0", + "reports/runtime_permission_probes.json summary.failure_count == 0", + "reports/runtime_permission_probes.json summary.installer_enforcement_pass_count records local installer enforcement but does not replace native evidence", + "reports/skill_os2_audit.json item native-permission-enforcement status becomes pass", + ], + "evidence_artifacts": [ + "dist/targets/*/adapter.json", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/install_simulation.json", + "reports/install_simulation.md", + "security/permission_policy.json", + ], + "privacy_contract": [ + "Do not mark native_enforcement true for metadata-only fallbacks.", + "Keep residual risks visible for targets that still rely on operator enforcement.", + ], + }, + "native-client-telemetry": { + "category": "external", + "owner": "Browser/Chrome/IDE/provider client integrator", + "objective": "Import production metadata-only events from a real external client into the local drift loop.", + "runbook": [ + "python3 scripts/telemetry_native_host.py . --write-launcher /tmp/yao-telemetry-host.sh --write-manifest /tmp/yao-telemetry-host.json --allowed-origin chrome-extension:///", + "Install the generated native messaging manifest for the real client and send at least one accepted skill_activation or skill_output event.", + "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl", + "python3 scripts/yao.py skill-atlas --workspace-root .", + "python3 scripts/yao.py skill-os2-audit . --generated-at ", + ], + "success_checks": [ + "reports/adoption_drift_report.json summary.source_types.external > 0", + "reports/adoption_drift_report.json summary.adoption_sample_count > 0", + "reports/skill_os2_audit.json item native-client-telemetry status becomes pass", + ], + "evidence_artifacts": [ + "reports/adoption_drift_report.json", + "reports/adoption_drift_report.md", + "reports/telemetry_hook_recipes.json", + "scripts/telemetry_native_host.py", + ], + "privacy_contract": [ + "Telemetry must remain metadata-only and local-first.", + "Do not package reports/telemetry_events.jsonl or any raw prompt, output, transcript, note, or message field.", + ], + }, +} + + +def rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def build_task(item: dict[str, Any]) -> dict[str, Any]: + template = TASK_TEMPLATES.get( + item["key"], + { + "category": "review", + "owner": "release reviewer", + "objective": item.get("target", "Collect stronger evidence for this non-pass audit item."), + "runbook": ["Open reports/skill_os2_audit.md and resolve the listed next action."], + "success_checks": [f"reports/skill_os2_audit.json item {item['key']} status becomes pass"], + "evidence_artifacts": [entry["path"] for entry in item.get("evidence", []) if entry.get("exists")], + "privacy_contract": ["Do not add raw private user content to release evidence."], + }, + ) + intake_runbook = [ + f"Copy evidence/world_class/templates/{item['key']}.intake.json to evidence/world_class/submissions/{item['key']}.json and fill only real evidence fields.", + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", + ] + intake_artifacts = [ + "evidence/world_class/intake.schema.json", + f"evidence/world_class/templates/{item['key']}.intake.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_intake.md", + ] + return { + "key": item["key"], + "label": item["label"], + "status": item["status"], + "category": template["category"], + "owner": template["owner"], + "current": item["current"], + "objective": template["objective"], + "runbook": [*template["runbook"], *intake_runbook], + "success_checks": template["success_checks"], + "evidence_artifacts": [*template["evidence_artifacts"], *intake_artifacts], + "privacy_contract": template["privacy_contract"], + "audit_next_action": item["next_action"], + } + + +def has_accepted_ledger_submission(skill_dir: Path, task: dict[str, Any], submissions_dir: Path) -> bool: + path = submissions_dir / f"{task['key']}.json" + payload, load_status = load_json_with_status(path) + if load_status != "present": + return False + validation = validate_payload(payload, task, path=path, root=skill_dir, template_expected=False) + return validation.get("status") == "pass" + + +def build_plan(skill_dir: Path, generated_at: str, submissions_dir: Path | None = None) -> dict[str, Any]: + audit = build_audit(skill_dir, generated_at) + submissions_dir = submissions_dir or (skill_dir / "evidence" / "world_class" / "submissions") + evidence_keys = set(TASK_TEMPLATES) + evidence_requirements = [build_task(item) for item in audit["items"] if item["key"] in evidence_keys] + tasks = [ + task + for task in evidence_requirements + if task["status"] != "pass" or not has_accepted_ledger_submission(skill_dir, task, submissions_dir) + ] + category_counts: dict[str, int] = {} + for task in tasks: + category_counts[task["category"]] = category_counts.get(task["category"], 0) + 1 + audit_world_class_ready = audit["summary"].get("world_class_ready") is True + return { + "schema_version": "1.0", + "ok": audit["ok"], + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": { + "audit_decision": audit["summary"]["decision"], + "world_class_ready": bool(audit["summary"]["world_class_ready"]), + "audit_world_class_ready": audit_world_class_ready, + "ready_to_claim_world_class": False, + "ledger_completion_required": True, + "evidence_requirement_count": len(evidence_requirements), + "task_count": len(tasks), + "human_task_count": category_counts.get("human", 0), + "external_task_count": category_counts.get("external", 0), + "review_task_count": category_counts.get("review", 0), + "decision": "audit-ready-ledger-required" if audit_world_class_ready and not tasks else "collect-external-evidence", + }, + "tasks": tasks, + "evidence_requirements": evidence_requirements, + "source_audit": { + "json": "reports/skill_os2_audit.json", + "markdown": "reports/skill_os2_audit.md", + "open_gap_count": audit["summary"]["open_gap_count"], + }, + "artifacts": { + "json": "reports/world_class_evidence_plan.json", + "markdown": "reports/world_class_evidence_plan.md", + "ledger": "reports/world_class_evidence_ledger.md", + "intake": "reports/world_class_evidence_intake.md", + }, + } + + +def render_markdown(plan: dict[str, Any]) -> str: + summary = plan["summary"] + lines = [ + "# World-Class Evidence Plan", + "", + f"Generated at: `{plan['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- audit decision: `{summary['audit_decision']}`", + f"- ready to claim world-class: `{str(summary['ready_to_claim_world_class']).lower()}`", + f"- ledger completion required: `{str(summary.get('ledger_completion_required', True)).lower()}`", + f"- evidence requirements: `{summary.get('evidence_requirement_count', 0)}`", + f"- tasks: `{summary['task_count']}`", + f"- human tasks: `{summary['human_task_count']}`", + f"- external tasks: `{summary['external_task_count']}`", + "", + "This report is an execution plan for the remaining world-class evidence gaps. It does not count a plan or source-report pass as completion; the ledger must still validate accepted submissions.", + "", + "## Task Table", + "", + "| Task | Status | Category | Owner | Current |", + "| --- | --- | --- | --- | --- |", + ] + for task in plan["tasks"]: + current = str(task["current"]).replace("|", "\\|") + lines.append( + f"| `{task['key']}` | `{task['status']}` | `{task['category']}` | {task['owner']} | {current} |" + ) + if not plan["tasks"]: + lines.append("| `none` | `pass` | `none` | none | audit gaps closed; ledger validation still required |") + for task in plan["tasks"]: + lines.extend( + [ + "", + f"## {task['label']}", + "", + f"- objective: {task['objective']}", + f"- audit next action: {task['audit_next_action']}", + "", + "### Runbook", + "", + ] + ) + for command in task["runbook"]: + lines.append(f"- `{command}`" if command.startswith("python3 ") or "=" in command else f"- {command}") + lines.extend(["", "### Success Checks", ""]) + lines.extend(f"- {check}" for check in task["success_checks"]) + lines.extend(["", "### Evidence Artifacts", ""]) + lines.extend(f"- `{artifact}`" for artifact in task["evidence_artifacts"]) + lines.extend(["", "### Privacy Contract", ""]) + lines.extend(f"- {item}" for item in task["privacy_contract"]) + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a world-class evidence collection plan.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json", default="reports/world_class_evidence_plan.json") + parser.add_argument("--output-md", default="reports/world_class_evidence_plan.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + plan = build_plan(skill_dir, args.generated_at) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(plan, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(plan), encoding="utf-8") + print(json.dumps(plan, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_world_class_operator_runbook.py b/scripts/render_world_class_operator_runbook.py new file mode 100644 index 0000000..71077c8 --- /dev/null +++ b/scripts/render_world_class_operator_runbook.py @@ -0,0 +1,651 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + +from html_rendering import html_text +from render_world_class_evidence_intake import build_intake +from render_world_class_evidence_ledger import build_ledger +from render_world_class_preflight import build_preflight +from render_world_class_submission_review import build_submission_review +from world_class_operator_runbook_coordination import build_coordination_plan, build_release_gate + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders an operator runbook for collecting pending world-class evidence without accepting evidence." + + +def rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def by_key(items: list[dict[str, Any]], key_name: str) -> dict[str, dict[str, Any]]: + return {str(item.get(key_name, "")): item for item in items if item.get(key_name)} + + +def output_path(path: str, skill_dir: Path) -> str: + candidate = Path(path) + if candidate.is_absolute(): + return rel_path(candidate, skill_dir) + return path + + +def build_runbook_item( + entry: dict[str, Any], + checklist: dict[str, Any], + review_item: dict[str, Any], + preflight_item: dict[str, Any], +) -> dict[str, Any]: + accepted = entry.get("status") == "accepted" or review_item.get("review_state") == "accepted" + commands = checklist.get("commands", {}) if isinstance(checklist.get("commands", {}), dict) else {} + must_collect = checklist.get("must_collect", {}) if isinstance(checklist.get("must_collect", {}), dict) else {} + source_checklist = review_item.get("source_checklist", []) + blocked_source_checks = [ + row for row in source_checklist if isinstance(row, dict) and row.get("status") != "pass" + ] + next_source_actions = [] + for row in blocked_source_checks: + action = str(row.get("next_action", "")).strip() + if action and action not in next_source_actions: + next_source_actions.append(action) + repair_checklist = ( + preflight_item.get("repair_checklist", []) if isinstance(preflight_item.get("repair_checklist", []), list) else [] + ) + phase_queue = preflight_item.get("phase_queue", []) if isinstance(preflight_item.get("phase_queue", []), list) else [] + if accepted: + repair_checklist = [] + phase_queue = [] + return { + "evidence_key": entry.get("key", ""), + "label": entry.get("label", entry.get("key", "")), + "category": entry.get("category", "external"), + "owner": entry.get("owner", "release reviewer"), + "ledger_status": entry.get("status", "pending"), + "intake_readiness": checklist.get("readiness", "missing"), + "review_state": review_item.get("review_state", "missing"), + "source_accepted": review_item.get("source_accepted") is True, + "objective": entry.get("objective", ""), + "current": entry.get("current", ""), + "execution_runbook": entry.get("runbook", []), + "blocking_reason": review_item.get("blocking_reason") or checklist.get("blocking_reason") or entry.get("next_action", ""), + "submission_path": checklist.get("submission_path") or entry.get("submission_state", {}).get("path", ""), + "template_path": checklist.get("template_path", ""), + "commands": { + "prepare_submission": commands.get("prepare_submission", ""), + "validate_intake": commands.get("validate_intake", ""), + "review_queue": commands.get("submission_review", ""), + "refresh_ledger": commands.get("refresh_ledger", "python3 scripts/yao.py world-class-ledger ."), + "guard_claim": commands.get("guard_claim", "python3 scripts/yao.py world-class-claim-guard ."), + }, + "must_collect": { + "provenance_requirements": must_collect.get("provenance_requirements", entry.get("provenance_requirements", [])), + "success_checks": must_collect.get("success_checks", entry.get("success_checks", [])), + "privacy_contract": must_collect.get("privacy_contract", entry.get("privacy_contract", [])), + }, + "evidence_artifacts": entry.get("evidence_artifacts", []), + "observed_state": entry.get("observed_state", {}), + "source_checklist": source_checklist, + "blocked_source_check_count": len(blocked_source_checks), + "next_source_actions": next_source_actions, + "repair_checklist": repair_checklist, + "repair_blocked_count": sum( + 1 + for row in repair_checklist + if isinstance(row, dict) and row.get("status") != "ready" + ), + "repair_counts_as_completion": False, + "phase_queue": phase_queue, + "phase_queue_blocked_count": sum( + 1 + for row in phase_queue + if isinstance(row, dict) and row.get("status") != "ready" + ), + "phase_queue_counts_as_completion": False, + "submission_state": entry.get("submission_state", {}), + "anti_overclaim": entry.get("anti_overclaim", {}), + } + + +def build_operator_runbook(skill_dir: Path, generated_at: str, submissions_dir: Path | None = None) -> dict[str, Any]: + submissions_dir = submissions_dir or (skill_dir / "evidence" / "world_class" / "submissions") + ledger = build_ledger(skill_dir, generated_at, submissions_dir=submissions_dir) + intake = build_intake(skill_dir, generated_at, submissions_dir=submissions_dir) + review = build_submission_review(skill_dir, generated_at, submissions_dir=submissions_dir) + preflight = build_preflight(skill_dir, generated_at, submissions_dir=submissions_dir) + checklist_by_key = by_key(intake.get("operator_checklist", []), "evidence_key") + review_by_key = by_key(review.get("items", []), "evidence_key") + preflight_by_key = by_key(preflight.get("items", []), "evidence_key") + items = [ + build_runbook_item( + entry, + checklist_by_key.get(str(entry.get("key", "")), {}), + review_by_key.get(str(entry.get("key", "")), {}), + preflight_by_key.get(str(entry.get("key", "")), {}), + ) + for entry in ledger.get("entries", []) + ] + coordination_plan = build_coordination_plan(items) + release_gate = build_release_gate(skill_dir, ledger) + summary = ledger.get("summary", {}) + review_summary = review.get("summary", {}) + preflight_summary = preflight.get("summary", {}) if isinstance(preflight.get("summary", {}), dict) else {} + return { + "schema_version": "1.0", + "ok": True, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": { + "evidence_item_count": len(items), + "pending_count": summary.get("pending_count", 0), + "accepted_count": summary.get("accepted_count", 0), + "awaiting_submission_count": review_summary.get("awaiting_submission_count", 0), + "ready_for_ledger_review_count": review_summary.get("ready_for_ledger_review_count", 0), + "valid_packet_source_incomplete_count": review_summary.get("valid_packet_source_incomplete_count", 0), + "invalid_submission_count": review_summary.get("invalid_submission_count", 0), + "source_check_count": review_summary.get("source_check_count", 0), + "source_pass_count": review_summary.get("source_pass_count", 0), + "source_blocked_count": review_summary.get("source_blocked_count", 0), + "repair_checklist_count": preflight_summary.get("repair_checklist_count", 0), + "repair_blocked_count": preflight_summary.get("repair_blocked_count", 0), + "phase_queue_count": preflight_summary.get("phase_queue_count", 0), + "phase_queue_blocked_count": preflight_summary.get("phase_queue_blocked_count", 0), + "phase_queue_row_count": preflight_summary.get("phase_queue_row_count", 0), + "phase_queue_next_phase": preflight_summary.get("phase_queue_next_phase", ""), + "phase_queue_next_action_id": preflight_summary.get("phase_queue_next_action_id", ""), + "phase_queue_next_command": preflight_summary.get("phase_queue_next_command", ""), + "phase_queue_counts_as_completion": False, + "coordination_step_count": len(coordination_plan), + "coordination_user_required_step_count": sum(1 for step in coordination_plan if step.get("requires_user_input")), + "coordination_pending_evidence_keys": sorted( + key + for key in {str(step.get("evidence_key", "")) for step in coordination_plan} + if key + ), + "coordination_counts_as_completion": False, + "release_gate_ready": release_gate["ready"], + "release_gate_blocked_count": release_gate["blocked_count"], + "release_gate_check_count": release_gate["check_count"], + "release_gate_counts_as_completion": False, + "ready_to_claim_world_class": summary.get("ready_to_claim_world_class") is True, + "runbook_counts_as_completion": False, + "decision": "ready-for-completion-audit" if summary.get("ready_to_claim_world_class") is True else "collect-evidence", + }, + "submissions": { + "directory": rel_path(submissions_dir, skill_dir), + "runbook_counts_submission_as_completion": False, + }, + "items": items, + "coordination_plan": coordination_plan, + "release_gate": release_gate, + "repair_checklist": preflight.get("repair_checklist", []) + if isinstance(preflight.get("repair_checklist", []), list) + else [], + "phase_queue": preflight.get("phase_queue", []) if isinstance(preflight.get("phase_queue", []), list) else [], + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json", + "preflight": "reports/world_class_evidence_preflight.json", + "submission_review": "reports/world_class_submission_review.json", + "claim_guard": "reports/world_class_claim_guard.json", + }, + "artifacts": { + "json": "reports/world_class_operator_runbook.json", + "markdown": "reports/world_class_operator_runbook.md", + "html": "reports/world_class_operator_runbook.html", + }, + } + + +def list_lines(values: list[Any], empty: str) -> list[str]: + if not values: + return [f"- {empty}"] + return [f"- {value}" for value in values] + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# World-Class Operator Runbook", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- ready to claim world-class: `{str(summary['ready_to_claim_world_class']).lower()}`", + f"- runbook counts as completion: `{str(summary['runbook_counts_as_completion']).lower()}`", + f"- evidence items: `{summary['evidence_item_count']}`", + f"- pending: `{summary['pending_count']}`", + f"- awaiting submission: `{summary['awaiting_submission_count']}`", + f"- ready for ledger review: `{summary['ready_for_ledger_review_count']}`", + f"- phase queue: `{summary['phase_queue_blocked_count']}` blocked / `{summary['phase_queue_count']}` phases", + f"- phase queue rows: `{summary['phase_queue_row_count']}`", + f"- phase queue counts as completion: `{str(summary['phase_queue_counts_as_completion']).lower()}`", + f"- coordination steps: `{summary['coordination_user_required_step_count']}` user-required / `{summary['coordination_step_count']}` total", + f"- coordination pending keys: `{', '.join(summary['coordination_pending_evidence_keys'])}`", + f"- coordination counts as completion: `{str(summary['coordination_counts_as_completion']).lower()}`", + f"- release gate ready: `{str(summary['release_gate_ready']).lower()}`", + f"- release gate blocked checks: `{summary['release_gate_blocked_count']}` / `{summary['release_gate_check_count']}`", + f"- release gate counts as completion: `{str(summary['release_gate_counts_as_completion']).lower()}`", + "", + "This runbook coordinates evidence collection only. It does not accept submissions or make world-class completion true.", + "", + "## Fast Path", + "", + "1. Run the real external or human work for one evidence item.", + "2. Generate the matching submission draft.", + "3. Replace template-only fields with aggregate evidence and provenance.", + "4. Validate intake, review the queue, refresh the ledger, then run the claim guard.", + "", + ] + lines.extend( + [ + "## Coordination Plan", + "", + "| Step | Evidence | Owner | Needs user | User action | Assistant action | Command | Pass condition |", + "| --- | --- | --- | --- | --- | --- | --- | --- |", + ] + ) + for step in report.get("coordination_plan", []): + lines.append( + f"| `{step.get('step_id', '')}` | `{step.get('evidence_key', '') or 'all'}` | {step.get('owner', '')} | " + f"`{str(step.get('requires_user_input') is True).lower()}` | {step.get('user_action', '')} | " + f"{step.get('assistant_action', '')} | `{step.get('command', '')}` | {step.get('pass_condition', '')} |" + ) + lines.append("") + lines.extend( + [ + "## Phase Queue", + "", + "| Phase | Status | Rows | Blocked | Owners | Next action | Verify |", + "| --- | --- | ---: | ---: | --- | --- | --- |", + ] + ) + for row in report.get("phase_queue", []): + owners = ", ".join(str(owner) for owner in row.get("owners", [])) + lines.append( + f"| `{row.get('phase', '')}` | `{row.get('status', '')}` | `{row.get('row_count', 0)}` | " + f"`{row.get('blocked_count', 0)}` | {owners} | {row.get('next_action', '')} | `{row.get('verification_command', '')}` |" + ) + lines.append("") + lines.extend( + [ + "## Evidence Items", + "", + "| Evidence | Ledger | Intake | Review | Blocked checks | Next source action | Owner |", + "| --- | --- | --- | --- | ---: | --- | --- |", + ] + ) + for item in report["items"]: + next_action = item.get("next_source_actions", ["none"])[0] if item.get("next_source_actions") else "none" + lines.append( + f"| `{item['evidence_key']}` | `{item['ledger_status']}` | `{item['intake_readiness']}` | " + f"`{item['review_state']}` | `{item.get('blocked_source_check_count', 0)}` | {next_action} | {item['owner']} |" + ) + for item in report["items"]: + lines.extend( + [ + "", + f"## {item['label']}", + "", + f"- objective: {item['objective']}", + f"- blocking reason: {item['blocking_reason']}", + f"- blocked source checks: `{item.get('blocked_source_check_count', 0)}`", + f"- repair rows: `{item.get('repair_blocked_count', 0)}` blocked", + f"- phase queue: `{item.get('phase_queue_blocked_count', 0)}` blocked phases", + f"- submission: `{item['submission_path'] or 'missing'}`", + f"- template: `{item['template_path'] or 'missing'}`", + "", + "### Phase Queue", + "", + "| Phase | Status | Rows | Blocked | Next action |", + "| --- | --- | ---: | ---: | --- |", + ] + ) + item_phase_queue = item.get("phase_queue", []) + if item_phase_queue: + for row in item_phase_queue: + lines.append( + f"| `{row.get('phase', '')}` | `{row.get('status', '')}` | `{row.get('row_count', 0)}` | " + f"`{row.get('blocked_count', 0)}` | {row.get('next_action', '')} |" + ) + else: + lines.append("| No phase queue listed. | `n/a` | `0` | `0` | n/a |") + lines.extend( + [ + "", + "### Source Runbook", + "", + *list_lines(item.get("execution_runbook", []), "No source runbook listed."), + "", + "### Commands", + "", + ] + ) + for label, command in item["commands"].items(): + lines.append(f"- {label}: `{command}`") + lines.extend(["", "### Must Collect", ""]) + lines.extend(list_lines(item["must_collect"].get("provenance_requirements", []), "No provenance requirements listed.")) + lines.extend(["", "### Success Checks", ""]) + lines.extend(list_lines(item["must_collect"].get("success_checks", []), "No success checks listed.")) + lines.extend(["", "### Privacy Contract", ""]) + lines.extend(list_lines(item["must_collect"].get("privacy_contract", []), "No privacy contract listed.")) + lines.extend(["", "### Evidence Artifacts", ""]) + lines.extend(list_lines(item.get("evidence_artifacts", []), "No evidence artifacts listed.")) + lines.extend(["", "### Next Source Actions", ""]) + lines.extend(list_lines(item.get("next_source_actions", []), "No blocked source checks.")) + lines.extend( + [ + "", + "### Source Evidence Snapshot", + "", + "| Check | Current | Expected | Status | Next action |", + "| --- | --- | --- | --- | --- |", + ] + ) + source_rows = item.get("source_checklist", []) + if source_rows: + for row in source_rows: + lines.append( + f"| {row['label']} | `{row['actual']}` | `{row['expected']}` | `{row['status']}` | {row.get('next_action', '')} |" + ) + else: + lines.append("| No source checks listed. | `n/a` | `n/a` | `n/a` | n/a |") + release_gate = report.get("release_gate", {}) + lines.extend( + [ + "", + "## Release Gate", + "", + f"- decision: `{release_gate.get('decision', 'missing')}`", + f"- ready: `{str(release_gate.get('ready') is True).lower()}`", + f"- blocked checks: `{release_gate.get('blocked_count', 0)}` / `{release_gate.get('check_count', 0)}`", + f"- counts as completion: `{str(release_gate.get('counts_as_completion') is True).lower()}`", + f"- final manual check: {release_gate.get('final_manual_check', '')}", + "", + "| Check | Current | Expected | Status | Artifact |", + "| --- | --- | --- | --- | --- |", + ] + ) + for check in release_gate.get("checks", []): + status = "pass" if check.get("passed") is True else "blocked" + lines.append( + f"| {check.get('label', '')} | `{check.get('current', '')}` | `{check.get('expected', '')}` | " + f"`{status}` | `{check.get('artifact', '')}` |" + ) + lines.extend( + [ + "", + "## Boundary", + "", + "- Planned work, draft packets, metadata fallback, pending human decisions, and local command runners do not count as completion.", + "- Valid intake means ready for submission review; ledger review still requires passing source evidence.", + "- The world-class ledger and claim guard remain the source of truth.", + ] + ) + return "\n".join(lines).rstrip() + "\n" + + +def html_list(values: list[Any], empty: str) -> str: + if not values: + return f"
  • {html_text(empty)}
  • " + return "".join(f"
  • {html_text(value)}
  • " for value in values) + + +def html_source_checks(rows: list[dict[str, Any]]) -> str: + if not rows: + return "

    No source checks listed.

    " + items = [] + for row in rows: + items.append( + "
  • " + f"{html_text(row.get('label', ''))}" + f"{html_text(row.get('field', ''))}: {html_text(row.get('actual', ''))} / {html_text(row.get('expected', ''))}" + f"{html_text(row.get('next_action', ''))}" + "
  • " + ) + return "
      " + "".join(items) + "
    " + + +def html_phase_queue(rows: list[dict[str, Any]]) -> str: + if not rows: + return "

    No phase queue listed.

    " + items = [] + for row in rows: + items.append( + "
  • " + f"{html_text(row.get('label', row.get('phase', '')))}" + f"{html_text(row.get('phase', ''))}: {html_text(row.get('blocked_count', 0))} / {html_text(row.get('row_count', 0))}" + f"{html_text(row.get('next_action', ''))}" + "
  • " + ) + return "
      " + "".join(items) + "
    " + + +def render_html_coordination_plan(steps: list[dict[str, Any]]) -> str: + if not steps: + return "

    No coordination plan listed.

    " + rows = [] + for step in steps: + rows.append( + "" + f"{html_text(step.get('step_id', ''))}" + f"{html_text(step.get('evidence_key', '') or 'all')}" + f"{html_text(step.get('owner', ''))}" + f"{html_text(str(step.get('requires_user_input') is True).lower())}" + f"{html_text(step.get('user_action', ''))}" + f"{html_text(step.get('assistant_action', ''))}" + f"{html_text(step.get('command', ''))}" + f"{html_text(step.get('pass_condition', ''))}" + "" + ) + return "
    " + "".join(rows) + "
    StepEvidenceOwnerUserUser actionAssistant actionCommandPass condition
    " + + +def render_html_release_gate(release_gate: dict[str, Any]) -> str: + checks = release_gate.get("checks", []) if isinstance(release_gate.get("checks", []), list) else [] + rows = [] + for check in checks: + status = "pass" if check.get("passed") is True else "blocked" + rows.append( + f"" + f"{html_text(check.get('label', ''))}" + f"{html_text(check.get('current', ''))}" + f"{html_text(check.get('expected', ''))}" + f"{html_text(status)}" + f"{html_text(check.get('artifact', ''))}" + "" + ) + check_table = ( + "
    " + + "".join(rows) + + "
    CheckCurrentExpectedStatusArtifact
    " + if rows + else "

    No release checks listed.

    " + ) + return f""" +
    +
    Decision{html_text(release_gate.get('decision', 'missing'))}
    +
    Ready{html_text(str(release_gate.get('ready') is True).lower())}
    +
    Blocked{html_text(release_gate.get('blocked_count', 0))}/{html_text(release_gate.get('check_count', 0))}
    +
    Counts{html_text(str(release_gate.get('counts_as_completion') is True).lower())}
    +
    +

    {html_text(release_gate.get('final_manual_check', ''))}

    + {check_table} + """.strip() + + +def render_html_item(item: dict[str, Any]) -> str: + commands = "".join( + f"
  • {html_text(label.replace('_', ' '))}{html_text(command)}
  • " + for label, command in item["commands"].items() + ) + must_collect = item["must_collect"] + return f""" +
    +
    {html_text(item['category'])} · {html_text(item['review_state'])}

    {html_text(item['label'])}

    +

    {html_text(item['blocking_reason'])}

    +
    +
    Owner
    {html_text(item['owner'])}
    +
    Ledger
    {html_text(item['ledger_status'])}
    +
    Blocked
    {html_text(item.get('blocked_source_check_count', 0))}
    +
    Queue
    {html_text(item.get('phase_queue_blocked_count', 0))}
    +
    Submission
    {html_text(item['submission_path'])}
    +
    +

    Phase Queue

    {html_phase_queue(item.get('phase_queue', []))}
    +

    Source Runbook

      {html_list(item.get('execution_runbook', []), 'No source runbook listed.')}
    +

    Commands

      {commands}
    +
    +

    Must Collect

      {html_list(must_collect.get('provenance_requirements', []), 'No provenance requirements listed.')}
    +

    Success Checks

      {html_list(must_collect.get('success_checks', []), 'No success checks listed.')}
    +

    Privacy

      {html_list(must_collect.get('privacy_contract', []), 'No privacy contract listed.')}
    +
    +

    Next Source Actions

      {html_list(item.get('next_source_actions', []), 'No blocked source checks.')}
    +

    Source Evidence Snapshot

    {html_source_checks(item.get('source_checklist', []))}
    +
    + """.strip() + + +def render_html(report: dict[str, Any]) -> str: + summary = report["summary"] + stats = [ + ("Pending", summary["pending_count"]), + ("Awaiting", summary["awaiting_submission_count"]), + ("Ready", summary["ready_for_ledger_review_count"]), + ("Source", f"{summary.get('source_pass_count', 0)}/{summary.get('source_check_count', 0)}"), + ("Queue", f"{summary.get('phase_queue_blocked_count', 0)}/{summary.get('phase_queue_count', 0)}"), + ("Steps", f"{summary.get('coordination_user_required_step_count', 0)}/{summary.get('coordination_step_count', 0)}"), + ("Gate", f"{summary.get('release_gate_blocked_count', 0)}/{summary.get('release_gate_check_count', 0)}"), + ("Blocked", summary.get("source_blocked_count", 0)), + ("Invalid", summary["invalid_submission_count"]), + ] + stat_html = "".join(f"
    {html_text(label)}{html_text(value)}
    " for label, value in stats) + item_html = "".join(render_html_item(item) for item in report["items"]) + coordination_html = render_html_coordination_plan(report.get("coordination_plan", [])) + release_gate_html = render_html_release_gate(report.get("release_gate", {})) + return f""" + + + + + World-Class Operator Runbook + + + + +
    +
    + Evidence Operations +

    World-Class Operator Runbook

    +

    A single operating page for collecting the remaining human and external evidence. It coordinates action, but does not accept evidence or change the ledger.

    +
    {stat_html}
    +
    +

    Fast Path

    1. Run the real external or human work for one evidence item.
    2. Generate and fill the matching submission draft.
    3. Validate intake and inspect the submission review queue.
    4. Refresh the ledger and run the claim guard before making any completion claim.
    +

    Coordination Plan

    These steps tell the assistant what to run and where the user must supply real provider, reviewer, client, or telemetry input. Every row is operational guidance only and keeps counts_as_completion false.

    {coordination_html}
    +

    Phase Queue

    {html_phase_queue(report.get('phase_queue', []))}
    +

    Evidence Items

    {item_html}
    +

    Release Gate

    {release_gate_html}
    +

    Boundary

    • Planned work, draft packets, metadata fallback, pending human decisions, and local command runners do not count as completion.
    • Valid intake means ready for submission review; ledger review still requires passing source evidence.
    • The world-class ledger and claim guard remain the source of truth.
    +
    + + +""" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render an operator runbook for collecting pending world-class evidence.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--submissions-dir") + parser.add_argument("--output-json", default="reports/world_class_operator_runbook.json") + parser.add_argument("--output-md", default="reports/world_class_operator_runbook.md") + parser.add_argument("--output-html", default="reports/world_class_operator_runbook.html") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + submissions_dir = Path(args.submissions_dir).resolve() if args.submissions_dir else None + report = build_operator_runbook(skill_dir, args.generated_at, submissions_dir=submissions_dir) + outputs = { + "json": Path(args.output_json), + "markdown": Path(args.output_md), + "html": Path(args.output_html), + } + for key, path in outputs.items(): + if not path.is_absolute(): + path = skill_dir / path + path.parent.mkdir(parents=True, exist_ok=True) + report["artifacts"][key] = output_path(str(path), skill_dir) + if key == "json": + path.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + elif key == "markdown": + path.write_text(render_markdown(report), encoding="utf-8") + else: + path.write_text(render_html(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_world_class_preflight.py b/scripts/render_world_class_preflight.py new file mode 100644 index 0000000..0c69dbc --- /dev/null +++ b/scripts/render_world_class_preflight.py @@ -0,0 +1,471 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import shlex +from datetime import date +from pathlib import Path +from typing import Any + +from prepare_world_class_submission_kit import build_artifact_checklist +from render_world_class_evidence_intake import build_intake +from render_world_class_evidence_ledger import build_ledger +from render_world_class_submission_review import build_submission_review +from world_class_evidence_contract import rel_path +from world_class_phase_queue import build_phase_queue, summarize_phase_queue +from world_class_preflight_markdown import render_markdown +from world_class_preflight_layout import render_html +from world_class_repair_checklist import build_preflight_repair_checklist, summarize_repair_checklist +from world_class_source_checks import summarize_source_checklist + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders a preflight checklist for collecting pending world-class evidence without accepting evidence." + + +PREFLIGHT_SPECS: dict[str, list[dict[str, Any]]] = { + "provider-holdout": [ + { + "key": "output-cases", + "label": "Output eval cases", + "kind": "file", + "path": "evals/output/cases.jsonl", + "required": True, + "next_action": "Keep output holdout cases available before provider execution.", + }, + { + "key": "provider-runner", + "label": "Provider runner", + "kind": "file", + "path": "scripts/provider_output_eval_runner.py", + "required": True, + "next_action": "Use the provider runner instead of the local command runner for model-backed evidence.", + }, + { + "key": "provider-api-key", + "label": "Provider credential", + "kind": "env_any", + "names": ["OPENAI_API_KEY", "DEEPSEEK_API_KEY"], + "required": True, + "secret": True, + "next_action": "Set one provider API key in the operator shell, such as OPENAI_API_KEY or DEEPSEEK_API_KEY; never commit or print the value.", + }, + { + "key": "provider-model", + "label": "Provider model", + "kind": "env", + "name": "YAO_OUTPUT_EVAL_MODEL", + "required": False, + "default": "provider-specific model, or pass --provider-model", + "next_action": "Optionally set YAO_OUTPUT_EVAL_MODEL, or pass --provider-model for the selected provider.", + }, + ], + "human-adjudication": [ + { + "key": "review-kit", + "label": "Blind review kit", + "kind": "file", + "path": "reports/output_review_kit.html", + "required": True, + "next_action": "Open the blind review kit and record real reviewer choices with required rationale.", + }, + { + "key": "decision-template", + "label": "Decision template", + "kind": "file", + "path": "reports/output_review_decisions.json", + "required": True, + "next_action": "Import real A/B decisions with reviewer, reviewed_at, winner_variant, confidence, reason, and truthful blind-review attestation via `python3 scripts/yao.py output-review-import --input --blind-review-attested --run-adjudication`.", + }, + { + "key": "decision-importer", + "label": "Decision importer", + "kind": "file", + "path": "scripts/import_output_review_decisions.py", + "required": True, + "next_action": "Use the importer to reject raw content fields and normalize reviewer decisions before adjudication.", + }, + { + "key": "human-reviewer", + "label": "Human reviewer", + "kind": "human", + "required": True, + "next_action": "Assign a real reviewer identity before claiming human adjudication.", + }, + ], + "native-permission-enforcement": [ + { + "key": "permission-policy", + "label": "Permission policy", + "kind": "file", + "path": "security/permission_policy.json", + "required": True, + "next_action": "Keep approved high-permission capabilities explicit.", + }, + { + "key": "permission-probes", + "label": "Runtime probes", + "kind": "file", + "path": "reports/runtime_permission_probes.json", + "required": True, + "next_action": "Refresh runtime permission probes after packaging changes.", + }, + { + "key": "native-guard", + "label": "Native guard", + "kind": "external", + "required": True, + "next_action": "Attach a real target-client or external installer runtime guard; metadata fallback is not enough.", + }, + ], + "native-client-telemetry": [ + { + "key": "native-host", + "label": "Native telemetry host", + "kind": "file", + "path": "scripts/telemetry_native_host.py", + "required": True, + "next_action": "Use the native host to receive metadata-only client events.", + }, + { + "key": "hook-recipes", + "label": "Hook recipes", + "kind": "file", + "path": "reports/telemetry_hook_recipes.json", + "required": True, + "next_action": "Refresh telemetry hook recipes before external client installation.", + }, + { + "key": "external-client", + "label": "External client", + "kind": "external", + "required": True, + "next_action": "Install a real Browser, Chrome, IDE, or provider client that emits metadata-only events.", + }, + ], +} + + +def env_present(name: str) -> bool: + return bool(os.environ.get(name)) + + +def shell_path(path: Path, root: Path) -> str: + return shlex.quote(rel_path(path, root)) + + +def build_submission_commands(skill_dir: Path, submissions_dir: Path, evidence_key: str | None = None) -> dict[str, str]: + output_dir = shell_path(submissions_dir, skill_dir) + prepare = f"python3 scripts/yao.py world-class-submission-kit . --output-dir {output_dir}" + if evidence_key: + prepare = f"python3 scripts/yao.py world-class-submission-kit . --evidence-key {evidence_key} --output-dir {output_dir}" + prefilled_prepare = f"{prepare} --prefill-artifacts" + return { + "prepare_submission": prepare, + "prepare_prefilled_submission": prefilled_prepare, + "validate_intake": f"python3 scripts/yao.py world-class-intake . --submissions-dir {output_dir}", + "submission_review": f"python3 scripts/yao.py world-class-submission-review . --submissions-dir {output_dir}", + "refresh_ledger": f"python3 scripts/yao.py world-class-ledger . --submissions-dir {output_dir}", + "guard_claim": "python3 scripts/yao.py world-class-claim-guard .", + } + + +def build_artifact_role_contract(rows: list[dict[str, Any]]) -> dict[str, Any]: + submission_ref_rows = [row for row in rows if row.get("submission_ref_required") is True] + supporting_rows = [row for row in rows if row.get("submission_ref_required") is not True] + return { + "schema_version": "1.0", + "role_source": "world-class-submission-kit", + "counts_as_evidence": False, + "artifact_prefill_counts_as_evidence": False, + "submission_ref_total_count": len(submission_ref_rows), + "submission_ref_ready_count": sum(1 for row in submission_ref_rows if row.get("artifact_ref_ready")), + "supporting_evidence_total_count": len(supporting_rows), + "supporting_evidence_ready_count": sum(1 for row in supporting_rows if row.get("artifact_ref_ready")), + "roles": [ + { + "role": "submission-ref", + "label": "Submission refs", + "copy_to_artifact_refs": True, + "description": "Rows marked submission-ref are the aggregate paths expected in artifact_refs.", + }, + { + "role": "supporting-evidence", + "label": "Supporting evidence", + "copy_to_artifact_refs": False, + "description": "Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into artifact_refs.", + }, + ], + } + + +def artifact_role_contract_for_key(contract: dict[str, Any], rows: list[dict[str, Any]], evidence_key: str) -> dict[str, Any]: + item_rows = [row for row in rows if row.get("evidence_key") == evidence_key] + role_contract = build_artifact_role_contract(item_rows) + return { + **role_contract, + "schema_version": contract.get("schema_version", "1.0"), + "role_source": contract.get("role_source", "world-class-submission-kit"), + } + + +def build_precheck(skill_dir: Path, evidence_key: str, spec: dict[str, Any]) -> dict[str, Any]: + kind = str(spec.get("kind", "")) + required = spec.get("required") is True + row = { + "evidence_key": evidence_key, + "key": spec.get("key", ""), + "label": spec.get("label", ""), + "kind": kind, + "required": required, + "next_action": spec.get("next_action", ""), + "secret_value_redacted": spec.get("secret") is True, + } + if kind == "file": + path = skill_dir / str(spec.get("path", "")) + exists = path.exists() + row.update( + { + "path": rel_path(path, skill_dir), + "status": "pass" if exists else ("missing" if required else "optional"), + "actual": "present" if exists else "missing", + } + ) + return row + if kind == "env": + name = str(spec.get("name", "")) + present = env_present(name) + row.update( + { + "env": name, + "status": "pass" if present else ("missing" if required else "optional"), + "actual": "set" if present else "not-set", + "default": spec.get("default", ""), + } + ) + return row + if kind == "env_any": + names = [str(name) for name in spec.get("names", []) if str(name).strip()] + set_names = [name for name in names if env_present(name)] + row.update( + { + "env_any": names, + "status": "pass" if set_names else ("missing" if required else "optional"), + "actual": "set" if set_names else "not-set", + "set_count": len(set_names), + "set_envs": set_names, + } + ) + return row + if kind == "human": + row.update( + { + "status": "human-required", + "actual": "external-human-action", + } + ) + return row + if kind == "external": + row.update( + { + "status": "external-required", + "actual": "external-integration-required", + } + ) + return row + row.update({"status": "unknown", "actual": "unknown"}) + return row + + +def item_status(entry: dict[str, Any], prechecks: list[dict[str, Any]], source_rows: list[dict[str, Any]]) -> str: + if entry.get("status") == "accepted": + return "accepted" + blocking = {row.get("status") for row in prechecks if row.get("required") is True} + if "missing" in blocking or "external-required" in blocking: + return "blocked" + if "human-required" in blocking: + return "ready-for-human-review" + if any(row.get("status") != "pass" for row in source_rows): + return "ready-to-collect" + return "ready-for-submission" + + +def first_next_action(prechecks: list[dict[str, Any]], source_rows: list[dict[str, Any]]) -> str: + for row in prechecks: + if row.get("required") is True and row.get("status") != "pass": + return str(row.get("next_action", "")) + for row in source_rows: + if row.get("status") != "pass": + return str(row.get("next_action", "")) + return "Validate intake, refresh the ledger, and run the claim guard." + + +def build_preflight(skill_dir: Path, generated_at: str, submissions_dir: Path | None = None) -> dict[str, Any]: + submissions_dir = submissions_dir or (skill_dir / "evidence" / "world_class" / "submissions") + ledger = build_ledger(skill_dir, generated_at, submissions_dir=submissions_dir) + intake = build_intake(skill_dir, generated_at, submissions_dir=submissions_dir) + review = build_submission_review(skill_dir, generated_at, submissions_dir=submissions_dir) + review_by_key = {str(item.get("evidence_key", "")): item for item in review.get("items", [])} + intake_by_key = {str(item.get("evidence_key", "")): item for item in intake.get("operator_checklist", [])} + operator_items = intake.get("operator_checklist", []) + operator_items = operator_items if isinstance(operator_items, list) else [] + artifact_checklist = build_artifact_checklist(skill_dir, operator_items) + artifact_role_contract = build_artifact_role_contract(artifact_checklist) + items: list[dict[str, Any]] = [] + precheck_rows: list[dict[str, Any]] = [] + source_rows: list[dict[str, Any]] = [] + for entry in ledger.get("entries", []): + key = str(entry.get("key", "")) + prechecks = [build_precheck(skill_dir, key, spec) for spec in PREFLIGHT_SPECS.get(key, [])] + review_item = review_by_key.get(key, {}) + item_source_rows = review_item.get("source_checklist", entry.get("source_checklist", [])) + item_source_rows = item_source_rows if isinstance(item_source_rows, list) else [] + status = item_status(entry, prechecks, item_source_rows) + item_commands = build_submission_commands(skill_dir, submissions_dir, key) + item = { + "evidence_key": key, + "label": entry.get("label", key), + "category": entry.get("category", "external"), + "owner": entry.get("owner", "release reviewer"), + "status": status, + "ledger_status": entry.get("status", "pending"), + "intake_readiness": intake_by_key.get(key, {}).get("readiness", "missing"), + "review_state": review_item.get("review_state", "missing"), + "source_accepted": entry.get("source_accepted") is True, + "prechecks": prechecks, + "source_checklist": item_source_rows, + "next_action": first_next_action(prechecks, item_source_rows), + "submission_path": intake_by_key.get(key, {}).get("submission_path", ""), + "template_path": intake_by_key.get(key, {}).get("template_path", ""), + "commands": item_commands, + "submission_kit": { + "prepare_command": item_commands["prepare_submission"], + "prefill_command": item_commands["prepare_prefilled_submission"], + "output_dir": rel_path(submissions_dir, skill_dir), + "draft_path": intake_by_key.get(key, {}).get("submission_path", ""), + "template_path": intake_by_key.get(key, {}).get("template_path", ""), + "artifact_role_contract": artifact_role_contract_for_key( + artifact_role_contract, + artifact_checklist, + key, + ), + "drafts_count_as_evidence": False, + "artifact_prefill_counts_as_evidence": False, + }, + "runbook": entry.get("runbook", []), + } + items.append(item) + precheck_rows.extend(prechecks) + source_rows.extend(item_source_rows) + repair_checklist = build_preflight_repair_checklist(items) + repairs_by_key: dict[str, list[dict[str, Any]]] = {} + for row in repair_checklist: + key = str(row.get("evidence_key", "")) + repairs_by_key.setdefault(key, []).append(row) + for item in items: + item_repair_rows = repairs_by_key.get(str(item.get("evidence_key", "")), []) + item["repair_checklist"] = item_repair_rows + item["phase_queue"] = build_phase_queue(item_repair_rows) + precheck_status_counts: dict[str, int] = {} + for row in precheck_rows: + status = str(row.get("status", "unknown")) + precheck_status_counts[status] = precheck_status_counts.get(status, 0) + 1 + source_summary = summarize_source_checklist(source_rows) + repair_summary = summarize_repair_checklist(repair_checklist) + phase_queue = build_phase_queue(repair_checklist) + phase_queue_summary = summarize_phase_queue(phase_queue) + collection_ready_count = sum(1 for item in items if item["status"] in {"ready-to-collect", "ready-for-human-review", "ready-for-submission"}) + blocked_count = sum(1 for item in items if item["status"] == "blocked") + ready_to_claim = ledger.get("summary", {}).get("ready_to_claim_world_class") is True + summary = { + "evidence_item_count": len(items), + "precheck_count": len(precheck_rows), + "precheck_pass_count": precheck_status_counts.get("pass", 0), + "precheck_missing_count": precheck_status_counts.get("missing", 0), + "precheck_optional_count": precheck_status_counts.get("optional", 0), + "precheck_human_required_count": precheck_status_counts.get("human-required", 0), + "precheck_external_required_count": precheck_status_counts.get("external-required", 0), + "collection_ready_count": collection_ready_count, + "collection_blocked_count": blocked_count, + **source_summary, + **repair_summary, + **phase_queue_summary, + "pending_count": ledger.get("summary", {}).get("pending_count", 0), + "ready_to_claim_world_class": ready_to_claim, + "credential_value_exposed": False, + "preflight_counts_as_evidence": False, + "decision": "ready-for-completion-audit" if ready_to_claim else ("collection-preflight-blocked" if blocked_count else "ready-to-collect-evidence"), + } + return { + "schema_version": "1.0", + "ok": True, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + "summary": summary, + "status_counts": {item["status"]: sum(1 for candidate in items if candidate["status"] == item["status"]) for item in items}, + "precheck_status_counts": precheck_status_counts, + "items": items, + "prechecks": precheck_rows, + "source_checklist": source_rows, + "repair_checklist": repair_checklist, + "phase_queue": phase_queue, + "submissions": { + "directory": rel_path(submissions_dir, skill_dir), + "commands": build_submission_commands(skill_dir, submissions_dir), + "submission_kit_command": build_submission_commands(skill_dir, submissions_dir)["prepare_submission"], + "submission_kit_prefill_command": build_submission_commands(skill_dir, submissions_dir)[ + "prepare_prefilled_submission" + ], + "preflight_counts_submission_as_completion": False, + "drafts_count_as_evidence": False, + "artifact_prefill_counts_as_evidence": False, + "artifact_role_contract": artifact_role_contract, + }, + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json", + "submission_review": "reports/world_class_submission_review.json", + "operator_runbook": "reports/world_class_operator_runbook.json", + }, + "artifacts": { + "json": "reports/world_class_evidence_preflight.json", + "markdown": "reports/world_class_evidence_preflight.md", + "html": "reports/world_class_evidence_preflight.html", + }, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render collection preflight checks for pending world-class evidence.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--submissions-dir") + parser.add_argument("--output-json", default="reports/world_class_evidence_preflight.json") + parser.add_argument("--output-md", default="reports/world_class_evidence_preflight.md") + parser.add_argument("--output-html", default="reports/world_class_evidence_preflight.html") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + submissions_dir = Path(args.submissions_dir).resolve() if args.submissions_dir else None + report = build_preflight(skill_dir, args.generated_at, submissions_dir=submissions_dir) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + output_html = Path(args.output_html) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + if not output_html.is_absolute(): + output_html = skill_dir / output_html + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_html.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + output_html.write_text(render_html(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_world_class_submission_review.py b/scripts/render_world_class_submission_review.py new file mode 100644 index 0000000..ae55074 --- /dev/null +++ b/scripts/render_world_class_submission_review.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python3 +import argparse +import json +from datetime import date +from pathlib import Path +from typing import Any + +from render_world_class_evidence_intake import build_intake +from render_world_class_evidence_ledger import build_ledger +from world_class_source_checks import build_source_checklist, summarize_source_checklist + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Renders a read-only review queue for world-class evidence submissions before ledger acceptance." + +TOP_LEVEL_SUMMARY_FIELDS = [ + "decision", + "ready_to_claim_world_class", + "review_item_count", + "accepted_count", + "awaiting_submission_count", + "valid_packet_source_incomplete_count", + "ready_for_ledger_review_count", + "fix_submission_count", + "unmatched_submission_count", + "invalid_submission_count", + "source_check_count", + "source_pass_count", + "source_blocked_count", + "review_counts_submission_as_completion", +] + + +def rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def by_key(items: list[dict[str, Any]], key_name: str = "evidence_key") -> dict[str, dict[str, Any]]: + result: dict[str, dict[str, Any]] = {} + for item in items: + key = str(item.get(key_name, "")) + if key and key not in result: + result[key] = item + return result + + +def top_level_summary_mirrors(summary: dict[str, Any]) -> dict[str, Any]: + return {key: summary[key] for key in TOP_LEVEL_SUMMARY_FIELDS if key in summary} + + +def review_state(entry: dict[str, Any], intake_result: dict[str, Any] | None) -> tuple[str, str]: + if entry.get("status") == "accepted": + return "accepted", "Ledger already accepts this evidence item." + + submission = entry.get("submission_state", {}) if isinstance(entry.get("submission_state", {}), dict) else {} + submission_status = str(submission.get("status", "missing")) + if submission_status == "missing": + return "awaiting-submission", "No evidence packet has been submitted for review." + if submission_status in {"invalid-json", "invalid-contract"}: + return "fix-submission", "Submission exists but fails the ledger submission contract." + if intake_result is None: + return "fix-submission", "Submission exists but is missing from the intake validation results." + if intake_result.get("status") != "pass": + return "fix-submission", "Submission exists but failed intake validation." + + observed = entry.get("observed_state", {}) if isinstance(entry.get("observed_state", {}), dict) else {} + if observed.get("accepted") is True: + return "ready-for-ledger-review", "Submission and source evidence are ready for final ledger review." + return "source-evidence-incomplete", "Submission packet is valid, but the source evidence checks still do not pass." + + +def build_item(entry: dict[str, Any], intake_result: dict[str, Any] | None) -> dict[str, Any]: + state, reason = review_state(entry, intake_result) + submission = entry.get("submission_state", {}) if isinstance(entry.get("submission_state", {}), dict) else {} + observed = entry.get("observed_state", {}) if isinstance(entry.get("observed_state", {}), dict) else {} + source_checklist = build_source_checklist([entry]) + source_summary = summarize_source_checklist(source_checklist) + return { + "evidence_key": entry.get("key", ""), + "label": entry.get("label", entry.get("key", "")), + "category": entry.get("category", "external"), + "owner": entry.get("owner", "release reviewer"), + "ledger_status": entry.get("status", "pending"), + "submission_status": submission.get("status", "missing"), + "intake_status": intake_result.get("status", "missing") if intake_result else "missing", + "source_accepted": observed.get("accepted") is True, + "review_state": state, + "blocking_reason": reason, + "submission_path": submission.get("path", ""), + "submitted_by": submission.get("submitted_by", ""), + "submitted_at": submission.get("submitted_at", ""), + "artifact_ref_count": submission.get("artifact_ref_count", 0), + "intake_errors": intake_result.get("errors", []) if intake_result else [], + "observed_state": observed, + "source_checklist": source_checklist, + **source_summary, + "success_checks": entry.get("success_checks", []), + "privacy_contract": entry.get("privacy_contract", []), + "next_action": entry.get("next_action", ""), + } + + +def build_submission_review(skill_dir: Path, generated_at: str, submissions_dir: Path | None = None) -> dict[str, Any]: + submissions_dir = submissions_dir or (skill_dir / "evidence" / "world_class" / "submissions") + ledger = build_ledger(skill_dir, generated_at, submissions_dir=submissions_dir) + intake = build_intake(skill_dir, generated_at, submissions_dir=submissions_dir) + intake_by_key = by_key(intake.get("submissions", [])) + entries = ledger.get("entries", []) + items = [build_item(entry, intake_by_key.get(str(entry.get("key", "")))) for entry in entries] + known_keys = {str(entry.get("key", "")) for entry in entries} + unmatched = [item for item in intake.get("submissions", []) if str(item.get("evidence_key", "")) not in known_keys] + counts: dict[str, int] = {} + for item in items: + state = str(item.get("review_state", "unknown")) + counts[state] = counts.get(state, 0) + 1 + invalid_count = counts.get("fix-submission", 0) + len(unmatched) + ready_count = counts.get("ready-for-ledger-review", 0) + accepted_count = counts.get("accepted", 0) + source_incomplete_count = counts.get("source-evidence-incomplete", 0) + awaiting_count = counts.get("awaiting-submission", 0) + source_rows = [row for item in items for row in item.get("source_checklist", [])] + source_summary = summarize_source_checklist(source_rows) + if accepted_count == len(items) and items: + decision = "ledger-complete" + elif invalid_count: + decision = "fix-submissions" + elif ready_count: + decision = "review-ready" + elif source_incomplete_count: + decision = "source-evidence-incomplete" + else: + decision = "awaiting-submissions" + summary = { + "review_item_count": len(items), + "accepted_count": accepted_count, + "awaiting_submission_count": awaiting_count, + "valid_packet_source_incomplete_count": source_incomplete_count, + "ready_for_ledger_review_count": ready_count, + "fix_submission_count": counts.get("fix-submission", 0), + "unmatched_submission_count": len(unmatched), + "invalid_submission_count": invalid_count, + **source_summary, + "ready_to_claim_world_class": ledger.get("summary", {}).get("ready_to_claim_world_class") is True, + "review_counts_submission_as_completion": False, + "decision": decision, + } + return { + "schema_version": "1.0", + "ok": invalid_count == 0, + "generated_at": generated_at, + "skill_dir": rel_path(skill_dir, ROOT), + **top_level_summary_mirrors(summary), + "summary": summary, + "report_contract": { + "schema_version": "1.0", + "contract": "world-class-submission-review", + "top_level_mirrors_summary": True, + "summary_fields": TOP_LEVEL_SUMMARY_FIELDS, + "source_of_truth": "summary", + }, + "submissions": { + "directory": rel_path(submissions_dir, skill_dir), + "review_counts_submission_as_completion": False, + }, + "items": items, + "unmatched_submissions": unmatched, + "source_reports": { + "ledger": "reports/world_class_evidence_ledger.json", + "intake": "reports/world_class_evidence_intake.json", + }, + "artifacts": { + "json": "reports/world_class_submission_review.json", + "markdown": "reports/world_class_submission_review.md", + }, + } + + +def render_list(values: list[Any], empty: str) -> list[str]: + if not values: + return [f"- {empty}"] + return [f"- {value}" for value in values] + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# World-Class Submission Review", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- review items: `{summary['review_item_count']}`", + f"- accepted: `{summary['accepted_count']}`", + f"- awaiting submission: `{summary['awaiting_submission_count']}`", + f"- valid packet but source incomplete: `{summary['valid_packet_source_incomplete_count']}`", + f"- ready for ledger review: `{summary['ready_for_ledger_review_count']}`", + f"- fix submission: `{summary['fix_submission_count']}`", + f"- unmatched submissions: `{summary['unmatched_submission_count']}`", + f"- ready to claim world-class: `{str(summary['ready_to_claim_world_class']).lower()}`", + f"- review counts submission as completion: `{str(summary['review_counts_submission_as_completion']).lower()}`", + "", + "This report is a read-only reviewer queue. It does not accept evidence or make world-class completion true.", + "", + "## Queue", + "", + "| Evidence | Review state | Intake | Source accepted | Submission | Next action |", + "| --- | --- | --- | --- | --- | --- |", + ] + for item in report["items"]: + source_accepted = str(item.get("source_accepted") is True).lower() + next_action = str(item.get("next_action", "")).replace("|", "\\|") + lines.append( + f"| `{item['evidence_key']}` | `{item['review_state']}` | `{item['intake_status']}` | " + f"`{source_accepted}` | `{item['submission_status']}` | {next_action} |" + ) + if report.get("unmatched_submissions"): + lines.extend(["", "## Unmatched Submissions", ""]) + for item in report["unmatched_submissions"]: + errors = "; ".join(item.get("errors", [])) or "unknown evidence key" + lines.append(f"- `{item.get('path', '')}`: {errors}") + lines.extend(["", "## Details", ""]) + for item in report["items"]: + lines.extend( + [ + f"### {item['label']}", + "", + f"- review state: `{item['review_state']}`", + f"- blocking reason: {item['blocking_reason']}", + f"- ledger status: `{item['ledger_status']}`", + f"- submission status: `{item['submission_status']}`", + f"- intake status: `{item['intake_status']}`", + f"- source accepted: `{str(item['source_accepted']).lower()}`", + f"- submission path: `{item.get('submission_path') or 'missing'}`", + "", + "#### Source Checks", + "", + *render_list( + [ + f"{row['label']}: {row['actual']} / {row['expected']} => {row['status']}" + for row in item.get("source_checklist", []) + ], + "No source checks listed.", + ), + "", + "#### Completion Assertions", + "", + *render_list(item.get("success_checks", []), "No completion assertions listed."), + "", + "#### Intake Errors", + "", + *render_list(item.get("intake_errors", []), "No intake errors."), + "", + "#### Privacy Contract", + "", + *render_list(item.get("privacy_contract", []), "No privacy contract listed."), + "", + ] + ) + lines.extend( + [ + "## Boundary", + "", + "- A valid submission packet is not accepted evidence by itself.", + "- Planned work, metadata fallback, pending human review, and local command-runner output still do not count.", + "- The world-class ledger remains the source of truth for `ready_to_claim_world_class`.", + ] + ) + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a read-only review queue for world-class evidence submissions.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--submissions-dir") + parser.add_argument("--output-json", default="reports/world_class_submission_review.json") + parser.add_argument("--output-md", default="reports/world_class_submission_review.md") + parser.add_argument("--generated-at", default=date.today().isoformat()) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + submissions_dir = Path(args.submissions_dir).resolve() if args.submissions_dir else None + report = build_submission_review(skill_dir, args.generated_at, submissions_dir=submissions_dir) + output_json = Path(args.output_json) + output_md = Path(args.output_md) + if not output_json.is_absolute(): + output_json = skill_dir / output_json + if not output_md.is_absolute(): + output_md = skill_dir / output_md + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/resource_boundary_check.py b/scripts/resource_boundary_check.py new file mode 100644 index 0000000..fe7ea65 --- /dev/null +++ b/scripts/resource_boundary_check.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from context_sizer import estimate_tokens, read_text, TEXT_EXTS +from governance_check import compute_score, read_frontmatter + + +OPTIONAL_DIRS = ("references", "scripts", "assets", "evals", "templates", "reports", "input", "outputs") +IGNORED_RELATIVE_DIRS = { + Path("reports") / "release_snapshots", + Path("tests") / "tmp", + Path("tests") / "tmp_snapshot", + Path("tests") / "tmp_cli", +} +IGNORED_FILE_PATTERNS = { + "reports/benchmark_reproducibility*.json", + "reports/benchmark_reproducibility*.md", + "reports/context_budget*.json", + "reports/context_budget*.md", + "reports/evidence_consistency*.json", + "reports/evidence_consistency*.md", + "reports/review-studio*.html", + "reports/review-studio*.json", + "reports/review-viewer*.html", + "reports/review-viewer*.json", + "reports/skill-interpretation*.html", + "reports/skill-interpretation*.json", + "reports/skill-overview*.html", + "reports/skill-overview*.json", + "reports/world_class_evidence_preflight*.json", + "reports/world_class_evidence_preflight*.md", + "reports/world_class_evidence_preflight*.html", + "reports/*pattern-analysis*.md", + "reports/*research-plan*.md", +} +CANONICAL_PATHS = ( + "SKILL.md", + "manifest.json", + "agents", + "references", + "scripts", + "assets", + "evals", + "templates", + "reports", + "failures", + "tests", + "input", + "outputs", +) +CONTEXT_BUDGETS = { + "scaffold": 700, + "production": 1000, + "library": 1300, + "governed": 1300, +} +SKILL_BODY_BUFFER = 100 +SKILL_BODY_WARN_RATIO = 0.85 +DEFERRED_RESOURCE_DIRS = {"references", "scripts", "evals", "templates", "assets", "input", "outputs"} +DEFERRED_RESOURCE_WARN_TOKENS = 120_000 +DEFERRED_RESOURCE_WARN_DIR_TOKENS = 80_000 +SCRIPT_GOVERNANCE_REPORTS = ( + "reports/security_trust_report.json", + "reports/architecture_maintainability.json", + "reports/python_compatibility.json", +) + + +def has_files(path: Path) -> bool: + return path.exists() and any(child.is_file() for child in path.rglob("*")) + + +def should_ignore(path: Path, root: Path) -> bool: + rel = path.relative_to(root) + if any(rel == ignored or ignored in rel.parents for ignored in IGNORED_RELATIVE_DIRS): + return True + if any(rel.match(pattern) for pattern in IGNORED_FILE_PATTERNS): + return True + return len(rel.parts) >= 2 and rel.parts[0] == "tests" and rel.parts[1].startswith("tmp_") + + +def load_manifest(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def load_json(path: Path) -> dict: + 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 iter_relevant_files(root: Path) -> list[Path]: + files = [] + for entry in CANONICAL_PATHS: + path = root / entry + if path.is_file(): + files.append(path) + elif path.is_dir(): + files.extend(sorted(file for file in path.rglob("*") if file.is_file() and not should_ignore(file, root))) + return files + + +def budget_tier_for(manifest: dict) -> str: + for field in ("context_budget_tier", "lifecycle_stage", "maturity_tier"): + value = manifest.get(field) + if value in CONTEXT_BUDGETS: + return value + return "production" + + +def explicit_dir_reference(dirname: str, path: Path, skill_text: str, manifest: dict) -> bool: + lowered = skill_text.lower() + if dirname.lower() in lowered or f"{dirname}/" in lowered: + return True + for file in path.rglob("*"): + if file.is_file() and file.name.lower() in lowered: + return True + declared = manifest.get("factory_components") or [] + return dirname in declared + + +def quality_signal_points(root: Path, manifest: dict, governance_score: int) -> int: + points = governance_score + if has_files(root / "evals"): + points += 10 + if has_files(root / "reports"): + points += 10 + if has_files(root / "scripts"): + points += 5 + if has_files(root / "references"): + points += 5 + if (root / "agents" / "interface.yaml").exists() and manifest: + points += 5 + if has_files(root / "failures") or has_files(root / "tests"): + points += 5 + return points + + +def script_resource_governance(root: Path, expected_file_count: int) -> dict: + trust = load_json(root / "reports" / "security_trust_report.json") + architecture = load_json(root / "reports" / "architecture_maintainability.json") + python_compat = load_json(root / "reports" / "python_compatibility.json") + trust_summary = trust.get("summary", {}) if isinstance(trust.get("summary", {}), dict) else {} + architecture_summary = architecture.get("summary", {}) if isinstance(architecture.get("summary", {}), dict) else {} + python_summary = python_compat.get("summary", {}) if isinstance(python_compat.get("summary", {}), dict) else {} + + reasons = [] + missing = [] + checks = [ + ( + "trust report covers scripts", + trust.get("ok") is True + and int(trust_summary.get("script_count", 0) or 0) >= expected_file_count + and int(trust_summary.get("secret_findings", 0) or 0) == 0 + and int(trust_summary.get("help_smoke_failed_count", 0) or 0) == 0, + ), + ( + "architecture report has no script hotspots or blockers", + architecture.get("ok") is True + and int(architecture_summary.get("hotspot_count", 0) or 0) == 0 + and int(architecture_summary.get("blocker_count", 0) or 0) == 0, + ), + ( + "Python compatibility report has no issues", + python_compat.get("ok") is True + and int(python_summary.get("issue_count", 0) or 0) == 0, + ), + ] + for label, ok in checks: + if ok: + reasons.append(label) + else: + missing.append(label) + return { + "status": "governed" if not missing else "needs-review", + "evidence": list(SCRIPT_GOVERNANCE_REPORTS), + "reasons": reasons, + "missing": missing, + } + + +def deferred_dir_governance( + root: Path, + dirname: str, + payload: dict, + manifest: dict, + skill_text: str, +) -> dict: + file_count = int(payload.get("file_count", 0) or 0) + if dirname == "scripts": + governance = script_resource_governance(root, file_count) + governance.update( + { + "path": dirname, + "estimated_tokens": int(payload.get("estimated_tokens", 0) or 0), + "file_count": file_count, + "rationale": "Script resources are deterministic deferred tools, not initial-load prompt context.", + } + ) + return governance + + referenced = explicit_dir_reference(dirname, root / dirname, skill_text, manifest) + declared = dirname in (manifest.get("factory_components") or []) + governed = referenced or declared + return { + "path": dirname, + "status": "governed" if governed else "needs-review", + "estimated_tokens": int(payload.get("estimated_tokens", 0) or 0), + "file_count": file_count, + "evidence": ["SKILL.md", "manifest.json"], + "reasons": ["directory is explicitly referenced or declared as a factory component"] if governed else [], + "missing": [] if governed else ["directory is not referenced in SKILL.md or manifest factory_components"], + "rationale": "Deferred resources are acceptable when they are discoverable and intentionally part of the package contract.", + } + + +def deferred_resource_governance( + root: Path, + manifest: dict, + skill_text: str, + deferred_resource_dirs: dict[str, dict[str, int | str]], + large_deferred_resource_dirs: list[dict], +) -> dict: + governed_dirs = [ + deferred_dir_governance(root, str(item["path"]), item, manifest, skill_text) + for item in large_deferred_resource_dirs + ] + missing = [item for item in governed_dirs if item["status"] != "governed"] + return { + "status": "governed" if governed_dirs and not missing else ("not-required" if not governed_dirs else "needs-review"), + "large_dir_count": len(governed_dirs), + "governed_large_dir_count": len(governed_dirs) - len(missing), + "directories": governed_dirs, + "summary": ( + "Large deferred resources are indexed and backed by evidence." + if governed_dirs and not missing + else "No large deferred resource directory exceeds the per-dir threshold." + if not governed_dirs + else "One or more large deferred resource directories still need explicit governance evidence." + ), + } + + +def analyze_skill( + root: Path, + max_initial_tokens: int | None = None, + warn_skill_body_tokens: int | None = None, +) -> dict: + skill_md = root / "SKILL.md" + failures = [] + warnings = [] + manifest = load_manifest(root / "manifest.json") + + if not skill_md.exists(): + failures.append("Missing SKILL.md") + return {"ok": False, "failures": failures, "warnings": warnings} + + files = iter_relevant_files(root) + skill_body_tokens = 0 + other_tokens = 0 + initial_load_tokens = 0 + total_text_tokens = 0 + deferred_resource_tokens = 0 + deferred_resource_dirs: dict[str, dict[str, int | str]] = {} + for path in files: + if path.suffix and path.suffix not in TEXT_EXTS and path.name != "SKILL.md": + continue + text = read_text(path) + tokens = estimate_tokens(text) + total_text_tokens += tokens + rel = path.relative_to(root) + top_dir = rel.parts[0] if rel.parts else str(rel) + if rel == Path("SKILL.md"): + skill_body_tokens += tokens + initial_load_tokens += tokens + else: + other_tokens += tokens + if rel.parts[0] in {"agents"}: + initial_load_tokens += tokens + if top_dir in DEFERRED_RESOURCE_DIRS: + deferred_resource_tokens += tokens + current = deferred_resource_dirs.setdefault( + top_dir, + {"path": top_dir, "estimated_tokens": 0, "file_count": 0}, + ) + current["estimated_tokens"] = int(current["estimated_tokens"]) + tokens + current["file_count"] = int(current["file_count"]) + 1 + + budget_tier = budget_tier_for(manifest) + budget_limit = max_initial_tokens if max_initial_tokens is not None else CONTEXT_BUDGETS[budget_tier] + skill_body_limit = ( + warn_skill_body_tokens + if warn_skill_body_tokens is not None + else max(int(budget_limit * SKILL_BODY_WARN_RATIO), budget_limit - SKILL_BODY_BUFFER) + ) + + if initial_load_tokens > budget_limit: + failures.append( + f"Estimated initial-load tokens exceed budget: {initial_load_tokens} > {budget_limit}" + ) + if skill_body_tokens > skill_body_limit: + warnings.append(f"SKILL.md is getting heavy: {skill_body_tokens} estimated tokens.") + + skill_text = skill_md.read_text(encoding="utf-8") + unused_resource_dirs = [] + for dirname in OPTIONAL_DIRS: + path = root / dirname + if path.exists() and not has_files(path): + warnings.append(f"{dirname}/ exists but is empty.") + continue + if has_files(path) and not explicit_dir_reference(dirname, path, skill_text, manifest): + warnings.append( + f"{dirname}/ contains files but is not referenced in SKILL.md or declared in manifest factory_components." + ) + unused_resource_dirs.append(dirname) + + if other_tokens and skill_body_tokens / (skill_body_tokens + other_tokens) > 0.75: + warnings.append("Most text still lives in SKILL.md; consider moving detail into references/ or scripts/.") + + large_deferred_resource_dirs = [ + item + for item in sorted( + deferred_resource_dirs.values(), + key=lambda payload: int(payload["estimated_tokens"]), + reverse=True, + ) + if int(item["estimated_tokens"]) > DEFERRED_RESOURCE_WARN_DIR_TOKENS + ] + deferred_governance = deferred_resource_governance( + root, + manifest, + skill_text, + deferred_resource_dirs, + large_deferred_resource_dirs, + ) + + if deferred_resource_tokens > DEFERRED_RESOURCE_WARN_TOKENS and deferred_governance["status"] != "governed": + warnings.append( + "Deferred resource footprint is high: " + f"{deferred_resource_tokens} estimated tokens across references/scripts/evals. " + "Keep Review Studio warnings visible until the largest resource dirs are split, archived, or justified." + ) + + frontmatter = read_frontmatter(skill_md) + governance_score, _ = compute_score(root, manifest, frontmatter, skill_text, bool(manifest)) + signal_points = quality_signal_points(root, manifest, governance_score) + quality_density = round(signal_points / max(initial_load_tokens, 1) * 1000, 1) + + report = { + "ok": not failures, + "failures": failures, + "warnings": warnings, + "stats": { + "context_budget_tier": budget_tier, + "context_budget_limit": budget_limit, + "skill_body_tokens": skill_body_tokens, + "other_text_tokens": other_tokens, + "estimated_initial_load_tokens": initial_load_tokens, + "estimated_total_text_tokens": total_text_tokens, + "deferred_resource_tokens": deferred_resource_tokens, + "deferred_resource_warn_threshold": DEFERRED_RESOURCE_WARN_TOKENS, + "deferred_resource_dirs": sorted( + deferred_resource_dirs.values(), + key=lambda payload: int(payload["estimated_tokens"]), + reverse=True, + ), + "large_deferred_resource_dirs": large_deferred_resource_dirs, + "deferred_resource_governance": deferred_governance, + "relevant_file_count": len(files), + "unused_resource_dirs": unused_resource_dirs, + "quality_signal_points": signal_points, + "quality_density": quality_density, + }, + } + return report + + +def main() -> None: + parser = argparse.ArgumentParser(description="Check whether a skill package keeps resource boundaries under control.") + parser.add_argument("skill_dir") + parser.add_argument("--max-initial-tokens", type=int, default=None) + parser.add_argument("--warn-skill-body-tokens", type=int, default=None) + args = parser.parse_args() + + root = Path(args.skill_dir).resolve() + report = analyze_skill( + root, + max_initial_tokens=args.max_initial_tokens, + warn_skill_body_tokens=args.warn_skill_body_tokens, + ) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if report["failures"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/review_studio_action_evidence.py b/scripts/review_studio_action_evidence.py new file mode 100644 index 0000000..6fc684f --- /dev/null +++ b/scripts/review_studio_action_evidence.py @@ -0,0 +1,388 @@ +import html +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by review_studio_actions.py to keep world-class evidence action cards out of generic Review Studio action wiring." + + +def action_command_rows(commands: dict[str, Any]) -> list[dict[str, str]]: + command_labels = { + "prepare_submission": "准备提交", + "validate_intake": "校验入口", + "submission_review": "审查提交", + "refresh_ledger": "刷新台账", + "guard_claim": "声明守卫", + } + rows = [] + for key in ("prepare_submission", "validate_intake", "submission_review", "refresh_ledger", "guard_claim"): + command = str(commands.get(key, "")).strip() + if command: + rows.append({"key": key, "label": command_labels.get(key, key), "command": command}) + return rows + + +def first_text_items(*values: Any, limit: int = 4) -> list[str]: + for value in values: + if isinstance(value, list) and value: + return [str(item) for item in value[:limit]] + return [] + + +def artifact_role_rows(contract: dict[str, Any]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + roles = contract.get("roles", []) if isinstance(contract.get("roles", []), list) else [] + for role in roles: + if not isinstance(role, dict): + continue + role_name = str(role.get("role", "")).strip() + if role_name == "submission-ref": + ready_count = int(contract.get("submission_ref_ready_count", 0) or 0) + total_count = int(contract.get("submission_ref_total_count", 0) or 0) + elif role_name == "supporting-evidence": + ready_count = int(contract.get("supporting_evidence_ready_count", 0) or 0) + total_count = int(contract.get("supporting_evidence_total_count", 0) or 0) + else: + ready_count = 0 + total_count = 0 + rows.append( + { + "role": role_name, + "label": str(role.get("label", role_name)), + "ready_count": ready_count, + "total_count": total_count, + "copy_to_artifact_refs": role.get("copy_to_artifact_refs") is True, + "description": str(role.get("description", "")), + } + ) + return rows + + +def compact_artifact_role_contract(contract: dict[str, Any]) -> dict[str, Any]: + if not isinstance(contract, dict) or not contract: + return {} + return { + "role_source": str(contract.get("role_source", "")), + "counts_as_evidence": contract.get("counts_as_evidence") is True, + "artifact_prefill_counts_as_evidence": contract.get("artifact_prefill_counts_as_evidence") is True, + "submission_ref_ready_count": int(contract.get("submission_ref_ready_count", 0) or 0), + "submission_ref_total_count": int(contract.get("submission_ref_total_count", 0) or 0), + "supporting_evidence_ready_count": int(contract.get("supporting_evidence_ready_count", 0) or 0), + "supporting_evidence_total_count": int(contract.get("supporting_evidence_total_count", 0) or 0), + "roles": artifact_role_rows(contract), + } + + +def compact_phase_queue(rows: list[Any]) -> list[dict[str, Any]]: + compact_rows: list[dict[str, Any]] = [] + for row in rows: + if not isinstance(row, dict): + continue + compact_rows.append( + { + "phase": str(row.get("phase", "")), + "label": str(row.get("label", "")), + "priority": int(row.get("priority", 90) or 90), + "status": str(row.get("status", "blocked")), + "blocked_count": int(row.get("blocked_count", 0) or 0), + "row_count": int(row.get("row_count", 0) or 0), + "owners": [str(item) for item in row.get("owners", []) if str(item).strip()], + "evidence_keys": [str(item) for item in row.get("evidence_keys", []) if str(item).strip()], + "next_action_id": str(row.get("next_action_id", "")), + "next_action": str(row.get("next_action", "")), + "verification_command": str(row.get("verification_command", "")), + "counts_as_completion": row.get("counts_as_completion") is True, + } + ) + return compact_rows + + +def world_class_action_steps(data: dict[str, Any]) -> list[dict[str, Any]]: + ledger = data.get("world_class_evidence_ledger", {}) if isinstance(data, dict) else {} + entries = ledger.get("entries", []) if isinstance(ledger, dict) else [] + intake = data.get("world_class_evidence_intake", {}) if isinstance(data, dict) else {} + intake_items = intake.get("operator_checklist", []) if isinstance(intake, dict) else [] + intake_by_key = { + str(item.get("evidence_key", "")): item + for item in intake_items + if isinstance(item, dict) and str(item.get("evidence_key", "")).strip() + } + preflight = data.get("world_class_evidence_preflight", {}) if isinstance(data, dict) else {} + preflight_items = preflight.get("items", []) if isinstance(preflight, dict) else [] + preflight_by_key = { + str(item.get("evidence_key", "")): item + for item in preflight_items + if isinstance(item, dict) and str(item.get("evidence_key", "")).strip() + } + steps = [] + for entry in entries: + if not isinstance(entry, dict): + continue + key = str(entry.get("key", "")).strip() + if not key: + continue + checklist = intake_by_key.get(key, {}) + preflight_item = preflight_by_key.get(key, {}) + submission = entry.get("submission_state", {}) if isinstance(entry.get("submission_state", {}), dict) else {} + source_checks = entry.get("source_checklist", []) if isinstance(entry.get("source_checklist", []), list) else [] + blocked_checks = [ + { + "label": str(row.get("label", "")), + "field": str(row.get("field", "")), + "actual": row.get("actual", ""), + "expected": str(row.get("expected", "")), + "status": str(row.get("status", "blocked")), + "next_action": str(row.get("next_action", "")), + } + for row in source_checks + if isinstance(row, dict) and str(row.get("status", "")) != "pass" + ] + repair_rows = [] + for row in preflight_item.get("repair_checklist", []) if isinstance(preflight_item, dict) else []: + if not isinstance(row, dict): + continue + repair_rows.append( + { + "action_id": str(row.get("action_id", "")), + "repair_type": str(row.get("repair_type", "")), + "target": str(row.get("target", "")), + "phase": str(row.get("phase", "")), + "priority": int(row.get("priority", 90) or 90), + "owner": str(row.get("owner", "")), + "status": str(row.get("status", "blocked")), + "blocking_reason": str(row.get("blocking_reason", "")), + "next_action": str(row.get("next_action", "")), + "verification_command": str(row.get("verification_command", "")), + "counts_as_completion": row.get("counts_as_completion") is True, + } + ) + derived_pass_count = sum(1 for row in source_checks if isinstance(row, dict) and str(row.get("status", "")) == "pass") + runbook = entry.get("runbook", []) + if not runbook and isinstance(checklist.get("must_collect", {}), dict): + runbook = checklist["must_collect"].get("runbook", []) + must_collect = checklist.get("must_collect", {}) if isinstance(checklist.get("must_collect", {}), dict) else {} + submission_kit = ( + preflight_item.get("submission_kit", {}) + if isinstance(preflight_item.get("submission_kit", {}), dict) + else {} + ) + phase_queue = compact_phase_queue( + preflight_item.get("phase_queue", []) if isinstance(preflight_item.get("phase_queue", []), list) else [] + ) + steps.append( + { + "key": key, + "label": str(entry.get("label", key)), + "category": str(entry.get("category", "")), + "status": str(entry.get("status", "pending")), + "readiness": str(checklist.get("readiness", "")), + "current": str(entry.get("current", "")), + "next_action": str(entry.get("next_action") or checklist.get("next_action", "")), + "submission_path": str(submission.get("path") or checklist.get("submission_path", "")), + "template_path": str(checklist.get("template_path", "")), + "source_pass_count": int(entry.get("source_pass_count", 0) or derived_pass_count), + "source_blocked_count": int(entry.get("source_blocked_count", 0) or len(blocked_checks)), + "blocked_checks": blocked_checks, + "repair_rows": repair_rows, + "repair_blocked_count": sum(1 for row in repair_rows if row.get("status") != "ready"), + "repair_counts_as_completion": any(row.get("counts_as_completion") for row in repair_rows), + "phase_queue": phase_queue, + "phase_queue_blocked_count": sum(1 for row in phase_queue if row.get("status") != "ready"), + "phase_queue_counts_as_completion": any(row.get("counts_as_completion") for row in phase_queue), + "commands": action_command_rows(checklist.get("commands", {}) if isinstance(checklist.get("commands", {}), dict) else {}), + "runbook": [str(item) for item in runbook[:3]], + "provenance_requirements": first_text_items( + must_collect.get("provenance_requirements"), + entry.get("provenance_requirements"), + limit=3, + ), + "success_checks": first_text_items( + must_collect.get("success_checks"), + entry.get("success_checks"), + limit=9, + ), + "evidence_artifacts": first_text_items( + must_collect.get("evidence_artifacts"), + entry.get("evidence_artifacts"), + limit=5, + ), + "artifact_role_contract": compact_artifact_role_contract( + submission_kit.get("artifact_role_contract", {}) + if isinstance(submission_kit.get("artifact_role_contract", {}), dict) + else {} + ), + "privacy_contract": first_text_items( + must_collect.get("privacy_contract"), + entry.get("privacy_contract"), + limit=4, + ), + } + ) + return steps + + +def render_small_list(items: list[Any], empty: str, ordered: bool = False) -> str: + if not items: + return f"

    {html.escape(empty)}

    " + tag = "ol" if ordered else "ul" + rows = "".join(f"
  • {html.escape(str(item))}
  • " for item in items) + return f"<{tag}>{rows}" + + +def render_artifact_role_contract(contract: dict[str, Any]) -> str: + roles = contract.get("roles", []) if isinstance(contract, dict) else [] + if not roles: + return "

    暂无资产角色。

    " + rows = [] + for role in roles: + rows.append( + "
  • " + f"{html.escape(str(role.get('role', '')))}" + f"{html.escape(str(role.get('ready_count', 0)))} / {html.escape(str(role.get('total_count', 0)))} ready" + f"artifact_refs: {html.escape(str(role.get('copy_to_artifact_refs') is True).lower())}" + f"{html.escape(str(role.get('description', '')))}" + "
  • " + ) + source = html.escape(str(contract.get("role_source", ""))) + counts = html.escape(str(contract.get("counts_as_evidence") is True).lower()) + prefill = html.escape(str(contract.get("artifact_prefill_counts_as_evidence") is True).lower()) + return ( + f"

    source: {source}; counts as evidence: {counts}; " + f"prefill counts as evidence: {prefill}

    " + "
      " + + "".join(rows) + + "
    " + ) + + +def render_action_evidence_steps(steps: list[dict[str, Any]]) -> str: + if not steps: + return "" + cards = [] + for step in steps: + blocked_checks = [] + for check in step.get("blocked_checks", []): + blocked_checks.append( + "
  • " + f"{html.escape(str(check.get('label', '')))}" + f"{html.escape(str(check.get('field', '')))}: {html.escape(str(check.get('actual', '')))} / {html.escape(str(check.get('expected', '')))}" + f"{html.escape(str(check.get('next_action', '')))}" + "
  • " + ) + command_rows = [] + for command in step.get("commands", [])[:4]: + command_rows.append( + "
  • " + f"{html.escape(str(command.get('label', '')))}" + f"{html.escape(str(command.get('command', '')))}" + "
  • " + ) + runbook_rows = [f"
  • {html.escape(str(item))}
  • " for item in step.get("runbook", [])] + repair_rows = [] + for row in step.get("repair_rows", []): + repair_rows.append( + "
  • " + f"#{html.escape(str(row.get('priority', '')))} · {html.escape(str(row.get('phase', '')))} · {html.escape(str(row.get('repair_type', '')))}" + f"{html.escape(str(row.get('target', '')))}" + f"{html.escape(str(row.get('owner', '')))}" + f"{html.escape(str(row.get('blocking_reason', '')))}" + f"{html.escape(str(row.get('next_action', '')))}" + f"{html.escape(str(row.get('verification_command', '')))}" + "
  • " + ) + phase_rows = [] + for row in step.get("phase_queue", []): + phase_rows.append( + "
  • " + f"#{html.escape(str(row.get('priority', '')))} · {html.escape(str(row.get('phase', '')))}" + f"{html.escape(str(row.get('label', '')))}" + f"{html.escape(str(row.get('blocked_count', 0)))} / {html.escape(str(row.get('row_count', 0)))} blocked" + f"owners: {html.escape(', '.join(str(item) for item in row.get('owners', [])) or 'n/a')}" + f"evidence: {html.escape(', '.join(str(item) for item in row.get('evidence_keys', [])) or 'n/a')}" + f"{html.escape(str(row.get('next_action', '')))}" + f"{html.escape(str(row.get('verification_command', '')))}" + "
  • " + ) + cards.append( + "
    " + f"
    {html.escape(str(step.get('status', 'pending')))} · {html.escape(str(step.get('category', '')))}" + f"

    {html.escape(str(step.get('label', step.get('key', 'evidence'))))}

    " + f"

    {html.escape(str(step.get('current', '')))}

    " + f"
    提交
    {html.escape(str(step.get('submission_path', '')))}
    " + f"
    模板
    {html.escape(str(step.get('template_path', '')))}
    " + f"
    阻断
    {html.escape(str(step.get('source_blocked_count', 0)))} blocked / {html.escape(str(step.get('source_pass_count', 0)))} pass
    " + f"
    修复
    {html.escape(str(step.get('repair_blocked_count', 0)))} repair rows; counts as completion: {html.escape(str(step.get('repair_counts_as_completion') is True).lower())}
    " + f"
    阶段
    {html.escape(str(step.get('phase_queue_blocked_count', 0)))} blocked phases; counts as completion: {html.escape(str(step.get('phase_queue_counts_as_completion') is True).lower())}
    " + f"
    下一步
    {html.escape(str(step.get('next_action', '')))}
    " + "
    阻断检查
    " + + ( + "
      " + "".join(blocked_checks) + "
    " + if blocked_checks + else "

    暂无阻断检查。

    " + ) + + "
    " + "
    阶段队列" + + ( + "
      " + "".join(phase_rows) + "
    " + if phase_rows + else "

    暂无阶段队列。

    " + ) + + "
    " + "
    修复清单" + + ( + "
      " + "".join(repair_rows) + "
    " + if repair_rows + else "

    暂无修复项。

    " + ) + + "
    " + "
    操作命令" + + ( + "
      " + "".join(command_rows) + "
    " + if command_rows + else "

    暂无操作命令。

    " + ) + + "
    " + "
    首要步骤" + + ( + "
      " + "".join(runbook_rows) + "
    " + if runbook_rows + else "

    暂无首要步骤。

    " + ) + + "
    " + "
    采集契约" + "
    " + "
    来源要求
    " + + render_small_list(step.get("provenance_requirements", []), "暂无来源要求。") + + "
    " + "
    通过条件
    " + + render_small_list(step.get("success_checks", []), "暂无通过条件。") + + "
    " + "
    证据资产
    " + + render_small_list(step.get("evidence_artifacts", []), "暂无证据资产。") + + "
    " + "
    资产角色
    " + + render_artifact_role_contract(step.get("artifact_role_contract", {})) + + "
    " + "
    隐私边界
    " + + render_small_list(step.get("privacy_contract", []), "暂无隐私边界。") + + "
    " + "
    " + "
    " + "
    " + ) + return ( + "
    " + "

    证据采集

    " + "

    以下条目仍需真实外部或人工证据;提交文件、校验命令和阻断检查必须同时闭环。

    " + "
    " + + "".join(cards) + + "
    " + ) diff --git a/scripts/review_studio_actions.py b/scripts/review_studio_actions.py new file mode 100644 index 0000000..e0fd91b --- /dev/null +++ b/scripts/review_studio_actions.py @@ -0,0 +1,374 @@ +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 compact_excerpt(text: str, limit: int = 180) -> str: + normalized = " ".join(text.strip().split()) + if len(normalized) <= limit: + return normalized + return normalized[: limit - 1].rstrip() + "…" + + +def find_line_anchor(path: Path, patterns: list[str] | None = None) -> dict[str, Any]: + if not path.exists(): + return {"line": None, "matched_pattern": "", "excerpt": ""} + if not patterns: + return {"line": 1, "matched_pattern": "", "excerpt": ""} + try: + lines = path.read_text(encoding="utf-8", errors="replace").splitlines() + except OSError: + return {"line": 1, "matched_pattern": "", "excerpt": ""} + for pattern in patterns: + for index, line in enumerate(lines, start=1): + if pattern in line: + return { + "line": index, + "matched_pattern": pattern, + "excerpt": compact_excerpt(line), + } + first_line = compact_excerpt(lines[0]) if lines else "" + return {"line": 1, "matched_pattern": "", "excerpt": first_line} + + +def find_line(path: Path, patterns: list[str] | None = None) -> int | None: + return find_line_anchor(path, patterns).get("line") + + +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() + anchor = find_line_anchor(path, spec.get("patterns", [])) + refs.append( + { + "path": rel_path, + "label": str(spec.get("label", rel_path)), + "kind": str(spec.get("kind", "source")), + "line": anchor.get("line"), + "matched_pattern": anchor.get("matched_pattern", ""), + "excerpt": anchor.get("excerpt", ""), + "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 + reports/output_review_decisions.json", + "source_paths": [ + {"path": "evals/output/cases.jsonl", "label": "output eval cases", "kind": "eval", "patterns": ['"case_id"', '"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_evidence_preflight.md", "label": "world-class evidence preflight", "kind": "report", "patterns": ["# World-Class Evidence Preflight"]}, + {"path": "reports/world_class_evidence_preflight.html", "label": "world-class preflight HTML", "kind": "report", "patterns": ["World-Class Evidence Preflight"]}, + {"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( + { + "action_id": f"review-action-{gate_item['key']}", + "gate_key": gate_item["key"], + "title": gate_item["label"], + "label": gate_item["label"], + "status": gate_item["status"], + "priority": "blocker" if gate_item["status"] == "block" else "warning", + "next_step": guidance["summary"], + "summary": guidance["summary"], + "reason": guidance["why"], + "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 "

    暂无结构化 source refs;请先打开证据报告。

    " + 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"{html.escape(label)}" + else: + path_html = f"{html.escape(label)} · missing" + pattern = str(ref.get("matched_pattern", "")).strip() + excerpt = str(ref.get("excerpt", "")).strip() + anchor_bits = [html.escape(str(ref.get("label", "source"))), html.escape(str(ref.get("kind", "source")))] + if pattern: + anchor_bits.append("pattern: " + html.escape(pattern)) + meta_html = " · ".join(anchor_bits) + excerpt_html = f"
    {html.escape(excerpt)}
    " if excerpt else "" + items.append( + "
  • " + f"{path_html}" + f"{meta_html}" + f"{excerpt_html}" + "
  • " + ) + return "
      " + "".join(items) + "
    " + + +def render_review_actions(actions: list[dict[str, Any]]) -> str: + if not actions: + return "

    当前没有 blocker 或 warning。保持现有证据链即可。

    " + cards = [] + for item in actions: + link_html = f"打开证据" 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( + "
    " + f"
    {html.escape(status_label(item['status']))}

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

    " + f"

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

    " + f"{html.escape(item['why'])}" + f"
    修复位置
    {html.escape(item['source_fix'])}
    " + f"
    验证命令
    {html.escape(item['verification_command'])}
    " + f"{evidence_steps_html}" + f"{source_refs_html}" + f"
    {html.escape(item['evidence'])} {link_html}
    " + "
    " + ) + return "".join(cards) diff --git a/scripts/review_studio_data.py b/scripts/review_studio_data.py new file mode 100644 index 0000000..7aca304 --- /dev/null +++ b/scripts/review_studio_data.py @@ -0,0 +1,387 @@ +#!/usr/bin/env python3 +"""Data loading and insight card helpers for Review Studio.""" + +import json +from pathlib import Path +from typing import Any + +from skill_ir_paths import find_skill_ir_path as find_skill_ir_path_for_name + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to load Review Studio source reports and metric cards." + +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 latest_json_report(directory: Path) -> dict[str, Any]: + if not directory.exists(): + return {} + candidates = sorted(path for path in directory.glob("*.json") if path.is_file()) + if not candidates: + return {} + payload = load_json(candidates[-1]) + if payload: + try: + payload.setdefault("source_path", str(candidates[-1].resolve().relative_to(directory.parent.parent.parent.resolve()))) + except ValueError: + payload.setdefault("source_path", str(candidates[-1])) + return payload + + +def latest_report_path(skill_dir: Path, directory: Path, suffix: str) -> str: + if not directory.exists(): + return "" + candidates = sorted(path for path in directory.glob(f"*{suffix}") if path.is_file()) + if not candidates: + return "" + try: + return str(candidates[-1].resolve().relative_to(skill_dir.resolve())) + except ValueError: + return str(candidates[-1]) + + +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 find_skill_ir_path(skill_dir: Path) -> str: + frontmatter = parse_frontmatter(skill_dir / "SKILL.md") + name = str(frontmatter.get("name") or skill_dir.name) + return find_skill_ir_path_for_name(skill_dir, name) + + +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", + "output_execution": "reports/output_execution_runs.md", + "output_blind_review": "reports/output_blind_review_pack.md", + "output_review_kit": "reports/output_review_kit.md", + "output_review_kit_html": "reports/output_review_kit.html", + "output_review_decisions": "reports/output_review_decisions.json", + "output_review_adjudication": "reports/output_review_adjudication.md", + "benchmark_reproducibility": "reports/benchmark_reproducibility.md", + "skill_os2_coverage": "reports/skill_os2_coverage.md", + "runtime_conformance": "reports/conformance_matrix.md", + "trust_report": "reports/security_trust_report.md", + "python_compatibility": "reports/python_compatibility.md", + "architecture_maintainability": "reports/architecture_maintainability.md", + "permission_policy": "security/permission_policy.md", + "runtime_permissions": "reports/runtime_permission_probes.md", + "skill_atlas": "reports/skill_atlas.html", + "compiled_targets": "reports/compiled_targets.md", + "adoption_drift": "reports/adoption_drift_report.md", + "review_waivers": "reports/review_waivers.md", + "review_annotations": "reports/review_annotations.md", + "adaptation_proposals": "reports/adaptation_proposals.md", + "adaptation_approval_ledger": "reports/adaptation_approval_ledger.json", + "adaptation_regression": "reports/adaptation_regression_report.md", + "world_class_evidence_plan": "reports/world_class_evidence_plan.md", + "world_class_evidence_ledger": "reports/world_class_evidence_ledger.md", + "world_class_evidence_intake": "reports/world_class_evidence_intake.md", + "world_class_evidence_preflight": "reports/world_class_evidence_preflight.md", + "world_class_evidence_preflight_html": "reports/world_class_evidence_preflight.html", + "world_class_submission_review": "reports/world_class_submission_review.md", + "world_class_operator_runbook": "reports/world_class_operator_runbook.md", + "world_class_operator_runbook_html": "reports/world_class_operator_runbook.html", + "world_class_claim_guard": "reports/world_class_claim_guard.md", + "registry_audit": "reports/registry_audit.md", + "package_verification": "reports/package_verification.md", + "install_simulation": "reports/install_simulation.md", + "upgrade_check": "reports/upgrade_check.md", + "migration": "docs/migration-v2.md", + } + skill_ir_path = find_skill_ir_path(skill_dir) + if skill_ir_path: + rels["skill_ir"] = skill_ir_path + daily_md = latest_report_path(skill_dir, skill_dir / "reports" / "skillops" / "daily", ".md") + weekly_md = latest_report_path(skill_dir, skill_dir / "reports" / "skillops" / "weekly", ".md") + if daily_md: + rels["daily_skillops"] = daily_md + if weekly_md: + rels["weekly_curator"] = weekly_md + 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"), + "output_execution": load_json(reports / "output_execution_runs.json"), + "output_blind_review": load_json(reports / "output_blind_review_pack.json"), + "output_review_kit": load_json(reports / "output_review_kit.json"), + "output_review_adjudication": load_json(reports / "output_review_adjudication.json"), + "benchmark_reproducibility": load_json(reports / "benchmark_reproducibility.json"), + "skill_os2_coverage": load_json(reports / "skill_os2_coverage.json"), + "compiled_targets": load_json(reports / "compiled_targets.json"), + "conformance": load_json(reports / "conformance_matrix.json"), + "runtime_permissions": load_json(reports / "runtime_permission_probes.json"), + "trust": load_json(reports / "security_trust_report.json"), + "python_compatibility": load_json(reports / "python_compatibility.json"), + "architecture_maintainability": load_json(reports / "architecture_maintainability.json"), + "context_budget": load_json(reports / "context_budget.json"), + "promotion": load_json(reports / "promotion_decisions.json"), + "atlas": load_json(reports / "skill_atlas.json"), + "adoption_drift": load_json(reports / "adoption_drift_report.json"), + "daily_skillops": latest_json_report(reports / "skillops" / "daily"), + "weekly_curator": latest_json_report(reports / "skillops" / "weekly"), + "review_waivers": load_json(reports / "review_waivers.json"), + "review_annotations": load_json(reports / "review_annotations.json"), + "adaptation_proposals": load_json(reports / "adaptation_proposals.json"), + "adaptation_approval_ledger": load_json(reports / "adaptation_approval_ledger.json"), + "adaptation_regression": load_json(reports / "adaptation_regression_report.json"), + "world_class_evidence_ledger": load_json(reports / "world_class_evidence_ledger.json"), + "world_class_evidence_intake": load_json(reports / "world_class_evidence_intake.json"), + "world_class_evidence_preflight": load_json(reports / "world_class_evidence_preflight.json"), + "world_class_submission_review": load_json(reports / "world_class_submission_review.json"), + "world_class_operator_runbook": load_json(reports / "world_class_operator_runbook.json"), + "world_class_claim_guard": load_json(reports / "world_class_claim_guard.json"), + "registry": load_json(reports / "registry_audit.json"), + "package_verification": load_json(reports / "package_verification.json"), + "install_simulation": load_json(reports / "install_simulation.json"), + "upgrade_check": load_json(reports / "upgrade_check.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", {}) + output_execution = data["output_execution"].get("summary", {}) + output_blind = data["output_blind_review"].get("summary", {}) + output_review_kit = data["output_review_kit"].get("summary", {}) + output_review = data["output_review_adjudication"].get("summary", {}) + benchmark = data["benchmark_reproducibility"].get("summary", {}) + blueprint = data["skill_os2_coverage"].get("summary", {}) + extension_partial = int(blueprint.get("extension_partial_count", 0) or 0) + extension_planned = int(blueprint.get("extension_planned_count", 0) or 0) + compiled = data["compiled_targets"].get("summary", {}) + conformance = data["conformance"].get("summary", {}) + runtime_permissions = data["runtime_permissions"].get("summary", {}) + trust = data["trust"].get("summary", {}) + python_compat = data["python_compatibility"].get("summary", {}) + architecture = data["architecture_maintainability"].get("summary", {}) + atlas = data["atlas"].get("summary", {}) + adoption = data["adoption_drift"].get("summary", {}) + daily_skillops = data["daily_skillops"].get("summary", {}) + weekly_curator = data["weekly_curator"].get("summary", {}) + waivers = data["review_waivers"].get("summary", {}) + annotations = data["review_annotations"].get("summary", {}) + intake = data["world_class_evidence_intake"].get("summary", {}) + claim_guard = data["world_class_claim_guard"].get("summary", {}) + registry = data["registry"].get("package", {}) + package_verification = data["package_verification"].get("summary", {}) + install_simulation = data["install_simulation"].get("summary", {}) + upgrade = data["upgrade_check"].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": "Compiler", + "value": f"{compiled.get('pass_count', 0)}/{compiled.get('target_count', 0)}", + "detail": "target contracts compiled from Skill IR", + }, + { + "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": "Exec Runs", + "value": str(output_execution.get("variant_run_count", 0)), + "detail": ( + f"command {output_execution.get('command_executed_count', 0)}; " + f"model {output_execution.get('model_executed_count', 0)}; " + f"recorded {output_execution.get('recorded_fixture_count', 0)}" + ), + }, + { + "label": "Blind A/B", + "value": str(output_blind.get("pair_count", 0)), + "detail": "review pairs hide baseline vs with-skill labels", + }, + { + "label": "Review Kit", + "value": f"{output_review_kit.get('ready_for_adjudication_count', 0)}/{output_review_kit.get('case_count', 0)}", + "detail": f"pending {output_review_kit.get('pending_decision_count', 0)}; answer key hidden", + }, + { + "label": "Review A/B", + "value": f"{output_review.get('judgment_count', 0)}/{output_review.get('pair_count', 0)}", + "detail": f"adjudication decisions; pending {output_review.get('pending_count', 0)}", + }, + { + "label": "Public Claim", + "value": "ready" if benchmark.get("public_claim_ready") is True else "blocked", + "detail": f"{benchmark.get('public_claim_blocker_count', 0)} blockers; local reproducible {str(benchmark.get('reproducibility_ready', False)).lower()}", + }, + { + "label": "Blueprint", + "value": f"{blueprint.get('pass_count', 0)}/{blueprint.get('item_count', 0)}", + "detail": ( + f"2.0 coverage; extensions partial {extension_partial}, " + f"planned {extension_planned}; evidence pending {blueprint.get('world_class_evidence_pending_count', 0)}" + ), + }, + { + "label": "Runtime", + "value": f"{conformance.get('pass_count', 0)}/{conformance.get('target_count', 0)}", + "detail": "target conformance pass rate", + }, + { + "label": "Perm Probe", + "value": f"{runtime_permissions.get('metadata_fallback_count', 0)}/{runtime_permissions.get('target_count', 0)}", + "detail": ( + f"{runtime_permissions.get('native_enforcement_count', 0)} native; " + f"{runtime_permissions.get('installer_enforcement_pass_count', 0)} installer-enforced" + ), + }, + { + "label": "Trust", + "value": str(trust.get("secret_findings", 0)), + "detail": f"{trust.get('script_count', 0)} scripts scanned; secrets found", + }, + { + "label": "Py Compat", + "value": str(python_compat.get("issue_count", 0)), + "detail": f"{python_compat.get('file_count', 0)} files scanned for Python {python_compat.get('target_python', '3.11')}", + }, + { + "label": "Arch Debt", + "value": str(architecture.get("hotspot_count", 0)), + "detail": ( + f"{architecture.get('largest_file_lines', 0)} largest lines; " + f"{architecture.get('watchlist_count', 0)} watchlist; " + f"{architecture.get('early_watchlist_count', 0)} early; " + f"{architecture.get('command_handler_count', 0)} CLI handlers; " + f"{architecture.get('entrypoint_command_handler_count', 0)} in entrypoint" + ), + }, + { + "label": "Atlas", + "value": str(atlas.get("route_collision_count", 0)), + "detail": f"{atlas.get('skill_count', 0)} scanned skills; route collisions", + }, + { + "label": "Drift", + "value": str(adoption.get("risk_band", "n/a")), + "detail": f"{adoption.get('event_count', 0)} metadata events; {adoption.get('missed_trigger_count', 0)} missed triggers", + }, + { + "label": "Daily Ops", + "value": str(daily_skillops.get("proposal_count", 0)), + "detail": ( + f"{daily_skillops.get('decision', 'missing')}; " + f"approval {daily_skillops.get('approval_count', 0)}; " + f"release lock {str(daily_skillops.get('release_lock_ready', False)).lower()}" + ), + }, + { + "label": "Weekly Queue", + "value": str(weekly_curator.get("unique_opportunity_count", 0)), + "detail": ( + f"{weekly_curator.get('decision', 'missing')}; " + f"ready {weekly_curator.get('ready_for_approval_review_count', 0)}; " + f"top score {weekly_curator.get('top_score', 0)}" + ), + }, + { + "label": "Waivers", + "value": str(waivers.get("active_count", 0)), + "detail": f"{waivers.get('covered_gate_count', 0)} gates covered; human risk decisions", + }, + { + "label": "Intake", + "value": f"{intake.get('template_pass_count', 0)}/{intake.get('template_count', 0)}", + "detail": ( + f"{intake.get('valid_submission_count', 0)} valid submissions; " + f"{intake.get('invalid_submission_count', 0)} invalid" + ), + }, + { + "label": "Claim Guard", + "value": str(claim_guard.get("violation_count", 0)), + "detail": f"{claim_guard.get('claim_surface_count', 0)} public surfaces scanned", + }, + { + "label": "Notes", + "value": f"{annotations.get('open_count', 0)}/{annotations.get('annotation_count', 0)}", + "detail": f"{annotations.get('open_blocker_count', 0)} open blocker annotations", + }, + { + "label": "Registry", + "value": str(registry.get("version", "n/a")), + "detail": f"{len(registry.get('targets', []))} targets; {registry.get('license', 'no license')} license", + }, + { + "label": "Archive", + "value": "pass" if data["package_verification"].get("ok") else "n/a", + "detail": f"{package_verification.get('archive_entry_count', 0)} zip entries; package verification", + }, + { + "label": "Install", + "value": "pass" if data["install_simulation"].get("ok") else "n/a", + "detail": ( + f"{install_simulation.get('adapter_count', 0)} adapters; " + f"{install_simulation.get('installer_permission_enforced_count', 0)} permissions enforced; " + f"{install_simulation.get('installer_permission_failure_count', 0)} permission failures" + ), + }, + { + "label": "Upgrade", + "value": str(upgrade.get("recommended_bump", "n/a")), + "detail": f"declared {upgrade.get('declared_bump', 'n/a')}; {upgrade.get('breaking_change_count', 0)} breaking changes", + }, + ] + return cards diff --git a/scripts/review_studio_formatting.py b/scripts/review_studio_formatting.py new file mode 100644 index 0000000..a6d3134 --- /dev/null +++ b/scripts/review_studio_formatting.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +"""Formatting helpers for Review Studio panels.""" + +import html +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to format report dictionaries as audit UI panels." + + +LABELS = { + "actionable_owner_gap_count": "待处理 owner", + "actionable_route_collision_count": "待处理冲突", + "actionable_skill_count": "纳入审查", + "actionable_stale_count": "待处理过期", + "adoption_rate": "采用率", + "adapter_count": "Adapter", + "archive_entry_count": "Zip 条目", + "archive_present": "归档存在", + "archive_sha256": "归档哈希", + "actual_gate_count": "实际 Gate", + "answer_revealed_count": "答案揭示", + "baseline_pass_rate": "Baseline", + "breaking_change_count": "破坏变更", + "case_count": "案例数", + "claim_surface_count": "声明面", + "command_executed_count": "命令执行", + "compatibility_pass_count": "兼容通过", + "covered_gate_count": "覆盖 Gate", + "daily_report_count": "日报数", + "declared_bump": "声明版本", + "decision": "决策", + "delta": "增益", + "duplicate_gate_keys": "重复 Gate", + "event_count": "事件数", + "expected_gate_count": "期望 Gate", + "failure_count": "失败数", + "file_count": "文件数", + "fstring_311_violation_count": "F-string 3.11", + "gate_pass": "Gate", + "help_smoke_failed_count": "Help 失败", + "human_review_complete": "人审完成", + "install_simulated": "安装模拟", + "installer_enforcement_pass_count": "安装拦截", + "installer_enforcement_ready": "安装拦截就绪", + "installer_enforcement_source_status": "安装证据", + "installer_enforcement_target_count": "安装拦截目标", + "installer_permission_capability_count": "安装权限能力", + "installer_permission_enforced_count": "安装权限", + "installer_permission_failure_count": "安装权限失败", + "invalid_submission_count": "无效提交", + "item_count": "项目数", + "issue_count": "问题数", + "ledger_pending_count": "台账待补", + "ledger_ready_to_claim_world_class": "台账可声明", + "license": "License", + "local_blueprint_ready": "本地蓝图", + "metadata_fallback_count": "Metadata fallback", + "missed_trigger_count": "漏触发", + "missing_count": "缺失数", + "missing_gate_keys": "缺失 Gate", + "module_count": "模块数", + "model_executed_count": "模型执行", + "name": "名称", + "native_enforcement_count": "原生执行", + "network_script_count": "网络脚本", + "non_actionable_issue_count": "非行动项", + "open_blocker_count": "阻断批注", + "open_count": "开放批注", + "owner": "Owner", + "package_sha256": "包体哈希", + "pass_count": "通过数", + "pending_count": "待审", + "pending_answer_hidden_count": "答案隐藏", + "permission_capability_count": "权限能力", + "permission_target_count": "权限目标", + "provider_evidence_complete": "Provider 证据", + "public_claim_blocker_count": "声明阻断", + "public_claim_ready": "可公开声明", + "public_world_class_ready": "世界级", + "proposal_count": "提案数", + "proposal_review_count": "提案复核", + "reproducibility_ready": "本地复现", + "recorded_fixture_count": "记录样本", + "ready_for_approval_review_count": "待批准复核", + "ready_for_external_collection": "可收证据", + "ready_for_ledger_review": "可审台账", + "recommended_pr_count": "建议 PR", + "recommended_bump": "建议版本", + "release_lock_ready": "发布锁", + "rendered_gate_keys": "渲染 Gate", + "residual_risk_count": "残余风险", + "risk_band": "风险带", + "route_collision_count": "路由冲突", + "script_count": "脚本数", + "schema_present": "Schema", + "secret_findings": "Secret", + "skill_count": "Skill 数", + "submission_count": "提交数", + "syntax_error_count": "语法错误", + "target_count": "目标数", + "target_python": "目标 Python", + "targets": "目标平台", + "template_count": "模板数", + "template_pass_count": "模板通过", + "timing_observed_count": "计时样本", + "token_estimated_count": "估算 Token", + "token_observed_count": "真实 Token", + "trust_level": "信任级别", + "unknown_gate_keys": "未知 Gate", + "unweighted_gate_keys": "未加权 Gate", + "unique_opportunity_count": "唯一机会", + "variant_run_count": "运行数", + "valid_submission_count": "有效提交", + "version": "版本", + "violation_count": "违规数", + "warning_count": "警告数", + "with_skill_pass_rate": "With Skill", + "world_class_evidence_pending_count": "待补证据", + "world_class_ready": "世界级就绪", +} + + +def label_for_key(key: str) -> str: + return LABELS.get(key, key.replace("_", " ").title()) + + +def value_text(value: Any) -> str: + if value is None: + return "n/a" + if isinstance(value, bool): + return "是" if value else "否" + if isinstance(value, float): + return str(round(value, 3)).rstrip("0").rstrip(".") + if isinstance(value, list): + return ", ".join(value_text(item) for item in value) if value else "无" + if isinstance(value, dict): + parts = [] + for key, nested_value in list(value.items())[:6]: + parts.append(f"{label_for_key(str(key))}: {value_text(nested_value)}") + return ";".join(parts) if parts else "无" + return str(value) + + +def render_kv_grid( + payload: dict[str, Any], + keys: list[str], + empty: str, +) -> str: + if not payload: + return f"

    {html.escape(empty)}

    " + rows = [] + for key in keys: + if key not in payload: + continue + value = value_text(payload.get(key)) + value_html = html.escape(value) + if "sha256" in key or "hash" in key or "checksum" in key: + value_html = f"{value_html}" + rows.append( + "
    " + f"
    {html.escape(label_for_key(key))}
    " + f"
    {value_html}
    " + "
    " + ) + if not rows: + return f"

    {html.escape(empty)}

    " + return "
    " + "".join(rows) + "
    " + + +def render_gate_contract_panel(contract: dict[str, Any]) -> str: + return render_kv_grid( + contract, + [ + "ok", + "expected_gate_count", + "actual_gate_count", + "missing_gate_keys", + "unknown_gate_keys", + "duplicate_gate_keys", + "unweighted_gate_keys", + ], + "gate contract missing", + ) + + +def registry_package_summary(package: dict[str, Any]) -> dict[str, Any]: + if not package: + return {} + compatibility = package.get("compatibility", {}) if isinstance(package.get("compatibility"), dict) else {} + pass_count = sum(1 for status in compatibility.values() if status == "pass") + checksums = package.get("checksums", {}) if isinstance(package.get("checksums"), dict) else {} + return { + "name": package.get("name", ""), + "version": package.get("version", ""), + "maturity": package.get("maturity", ""), + "owner": package.get("owner", ""), + "license": package.get("license", ""), + "trust_level": package.get("trust_level", ""), + "targets": package.get("targets", []), + "compatibility_pass_count": f"{pass_count}/{len(compatibility)}", + "archive_sha256": checksums.get("archive_sha256", ""), + } diff --git a/scripts/review_studio_gate_contract.py b/scripts/review_studio_gate_contract.py new file mode 100644 index 0000000..d1ee04b --- /dev/null +++ b/scripts/review_studio_gate_contract.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Review Studio gate keys, scoring, and contract checks.""" + +from collections import Counter +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by review_studio_gates.py for stable gate scoring and contract checks." + + +GATE_WEIGHTS = { + "trigger-lab": 15, + "output-lab": 20, + "context-budget": 10, + "runtime-matrix": 10, + "trust-report": 10, + "python-compat": 10, + "architecture-maintainability": 10, + "permission-gates": 10, + "permission-runtime": 10, + "skill-atlas": 10, + "operations-loop": 10, + "review-waivers": 10, + "world-class-evidence": 10, + "registry-audit": 10, + "release-notes": 10, + "intent-canvas": 10, +} +REVIEW_STUDIO_GATE_KEYS = frozenset(GATE_WEIGHTS) + + +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 gate_contract(gates: list[dict[str, str]]) -> dict[str, Any]: + rendered_gate_keys = [str(item.get("key", "")) for item in gates] + rendered_gate_set = set(rendered_gate_keys) + expected_gate_set = set(REVIEW_STUDIO_GATE_KEYS) + duplicate_gate_keys = sorted(key for key, count in Counter(rendered_gate_keys).items() if count > 1) + missing_gate_keys = sorted(expected_gate_set - rendered_gate_set) + unknown_gate_keys = sorted(rendered_gate_set - expected_gate_set) + unweighted_gate_keys = sorted(rendered_gate_set - set(GATE_WEIGHTS)) + return { + "ok": not (missing_gate_keys or unknown_gate_keys or duplicate_gate_keys or unweighted_gate_keys), + "expected_gate_count": len(expected_gate_set), + "actual_gate_count": len(rendered_gate_keys), + "expected_gate_keys": sorted(expected_gate_set), + "rendered_gate_keys": rendered_gate_keys, + "missing_gate_keys": missing_gate_keys, + "unknown_gate_keys": unknown_gate_keys, + "duplicate_gate_keys": duplicate_gate_keys, + "unweighted_gate_keys": unweighted_gate_keys, + } + + +def min_output_cases(maturity: str) -> int: + if maturity in {"library", "governed"}: + return 5 + if maturity == "production": + return 3 + return 1 + + +def weighted_score(gates: list[dict[str, str]]) -> int: + earned = 0.0 + total = 0.0 + for item in gates: + weight = GATE_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 diff --git a/scripts/review_studio_gate_helpers.py b/scripts/review_studio_gate_helpers.py new file mode 100644 index 0000000..6ae7842 --- /dev/null +++ b/scripts/review_studio_gate_helpers.py @@ -0,0 +1,140 @@ +"""Helper functions for Review Studio gate evaluation.""" + +import json +import os +from pathlib import Path +from typing import Any + +try: + from trust_check import permission_governance_status as compute_permission_governance_status + from trust_check import script_inventory as trust_script_inventory +except ImportError: # pragma: no cover + compute_permission_governance_status = None + trust_script_inventory = None + +from review_studio_gate_contract import min_output_cases + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by review_studio_gates.py to keep reusable Review Studio gate helpers out of the main gate sequence." + + +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 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 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 fallback_permission_governance(skill_dir: Path) -> dict[str, Any]: + if compute_permission_governance_status is None or trust_script_inventory is None: + return {} + try: + scripts = trust_script_inventory(skill_dir) + return compute_permission_governance_status(skill_dir, scripts) + except Exception: + return {} + + +def build_output_lab_gate( + skill_dir: Path, + output_html: Path, + maturity: str, + output: dict[str, Any], + output_execution: dict[str, Any], + output_blind: dict[str, Any], + output_review: dict[str, Any], +) -> dict[str, str]: + output_summary = output.get("summary", {}) + output_execution_summary = output_execution.get("summary", {}) + output_blind_summary = output_blind.get("summary", {}) + output_review_summary = output_review.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) + blind_pair_count = int(output_blind_summary.get("pair_count", 0) or 0) + execution_variant_count = int(output_execution_summary.get("variant_run_count", 0) or 0) + execution_command_count = int(output_execution_summary.get("command_executed_count", 0) or 0) + execution_model_count = int(output_execution_summary.get("model_executed_count", 0) or 0) + execution_recorded_count = int(output_execution_summary.get("recorded_fixture_count", 0) or 0) + review_pair_count = int(output_review_summary.get("pair_count", 0) or 0) + review_judgment_count = int(output_review_summary.get("judgment_count", 0) or 0) + review_pending_count = int(output_review_summary.get("pending_count", 0) or 0) + review_invalid_count = int(output_review_summary.get("invalid_decision_count", 0) or 0) + production_like = maturity in {"production", "library", "governed"} + blind_missing = production_like and (not output_blind or blind_pair_count < case_count) + review_missing = production_like and case_count > 0 and not output_review + review_pending = production_like and bool(output_review) and review_pending_count > 0 + execution_failed = bool(output_execution) and ( + not output_execution.get("ok", True) or int(output_execution_summary.get("failure_count", 0) or 0) > 0 + ) + review_invalid = bool(output_review) and (not output_review.get("ok", True) or review_invalid_count > 0) + output_blocked = ( + not output.get("ok", False) + or not output_summary.get("gate_pass", False) + or case_count < required_cases + or execution_failed + or review_invalid + ) + output_warn = file_backed == 0 or near_neighbor == 0 or boundary == 0 or blind_missing or review_missing or review_pending + 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}; " + f"blind A/B {blind_pair_count}" + + ( + f"; exec {execution_variant_count}; command {execution_command_count}; " + f"model {execution_model_count}; recorded {execution_recorded_count}" + if output_execution + else "" + ) + + (f"; reviewed {review_judgment_count}/{review_pair_count}" if output_review else "") + + (f"; review pending {review_pending_count}" if review_pending else "") + + ("; review adjudication missing" if review_missing else "") + ) + return gate( + "output-lab", + "输出实验", + output_status, + output_detail, + "reports/output_quality_scorecard.json", + report_link(output_html, skill_dir, "reports/output_quality_scorecard.md"), + ) diff --git a/scripts/review_studio_gates.py b/scripts/review_studio_gates.py new file mode 100644 index 0000000..a253d6c --- /dev/null +++ b/scripts/review_studio_gates.py @@ -0,0 +1,543 @@ +#!/usr/bin/env python3 +"""Gate evaluation contract for Review Studio 2.0.""" + +from pathlib import Path +from typing import Any + +from review_studio_gate_contract import ( + GATE_WEIGHTS, + REVIEW_STUDIO_GATE_KEYS, + add_blockers_from_gate, + gate_contract, + min_output_cases, + status_label, + weighted_score, +) +from review_studio_gate_helpers import ( + build_output_lab_gate, + fallback_permission_governance, + gate, + report_link, + target_maturity, +) + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep Review Studio gate evaluation separate from HTML rendering." + + +ROOT = Path(__file__).resolve().parent.parent + + +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"), + ) + ) + + gates.append( + build_output_lab_gate( + skill_dir, + output_html, + maturity, + data["output_quality"], + data["output_execution"], + data["output_blind_review"], + data["output_review_adjudication"], + ) + ) + + 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" + large_deferred_dirs = context_stats.get("large_deferred_resource_dirs", []) or [] + top_deferred = "none" + if large_deferred_dirs: + first = large_deferred_dirs[0] + top_deferred = f"{first.get('path', 'resource')} {first.get('estimated_tokens', 'n/a')}" + deferred_governance = context_stats.get("deferred_resource_governance", {}) if isinstance(context_stats, dict) else {} + governance_status = deferred_governance.get("status", "unknown") if isinstance(deferred_governance, dict) else "unknown" + context_detail = ( + f"initial load {context_stats.get('estimated_initial_load_tokens', 'n/a')}/" + f"{context_stats.get('context_budget_limit', 'n/a')}; " + f"deferred {context_stats.get('deferred_resource_tokens', 'n/a')}/" + f"{context_stats.get('deferred_resource_warn_threshold', 'n/a')}; " + f"top deferred {top_deferred}; " + f"resource governance {governance_status}; " + f"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; " + f"{trust_summary.get('help_smoke_failed_count', 0)} help smoke failures" + ) + 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"), + ) + ) + + python_compat = data["python_compatibility"] + python_compat_summary = python_compat.get("summary", {}) + if not python_compat: + python_compat_status = "warn" + python_compat_detail = "Python compatibility report is missing" + else: + issue_count = int(python_compat_summary.get("issue_count", 0) or 0) + syntax_error_count = int(python_compat_summary.get("syntax_error_count", 0) or 0) + fstring_violation_count = int(python_compat_summary.get("fstring_311_violation_count", 0) or 0) + python_compat_status = ( + "block" + if not python_compat.get("ok", True) or issue_count or syntax_error_count or fstring_violation_count + else "pass" + ) + python_compat_detail = ( + f"Python {python_compat_summary.get('target_python', '3.11')}; " + f"{python_compat_summary.get('file_count', 0)} files; " + f"{issue_count} compatibility issues; " + f"{syntax_error_count} syntax; " + f"{fstring_violation_count} f-string 3.11 hazards" + ) + gates.append( + gate( + "python-compat", + "Python 兼容", + python_compat_status, + python_compat_detail, + "reports/python_compatibility.json", + report_link(output_html, skill_dir, "reports/python_compatibility.md"), + ) + ) + + architecture = data["architecture_maintainability"] + architecture_summary = architecture.get("summary", {}) + if not architecture: + if maturity in {"library", "governed"}: + architecture_status = "warn" + architecture_detail = "architecture maintainability report is missing for a reusable package" + else: + architecture_status = "pass" + architecture_detail = "architecture maintainability report is optional until code surface grows" + else: + hotspot_count = int(architecture_summary.get("hotspot_count", 0) or 0) + watchlist_count = int(architecture_summary.get("watchlist_count", 0) or 0) + early_watchlist_count = int(architecture_summary.get("early_watchlist_count", 0) or 0) + blocker_count = int(architecture_summary.get("blocker_count", 0) or 0) + architecture_status = "block" if not architecture.get("ok", True) or blocker_count else ("warn" if hotspot_count else "pass") + hotspot_label = "hotspot" if hotspot_count == 1 else "hotspots" + watchlist_label = "watchlist file" if watchlist_count == 1 else "watchlist files" + early_label = "early watch file" if early_watchlist_count == 1 else "early watch files" + blocker_label = "blocker" if blocker_count == 1 else "blockers" + architecture_detail = ( + f"{architecture_summary.get('python_file_count', 0)} Python files; " + f"{hotspot_count} {hotspot_label}; " + f"{watchlist_count} {watchlist_label}; " + f"{early_watchlist_count} {early_label}; " + f"{blocker_count} {blocker_label}; " + f"largest {architecture_summary.get('largest_file_lines', 0)} lines; " + f"{architecture_summary.get('command_handler_count', 0)} CLI handlers; " + f"{architecture_summary.get('entrypoint_command_handler_count', 0)} in entrypoint" + ) + gates.append( + gate( + "architecture-maintainability", + "架构维护", + architecture_status, + architecture_detail, + "reports/architecture_maintainability.json", + report_link(output_html, skill_dir, "reports/architecture_maintainability.md"), + ) + ) + + permission_governance = trust.get("permission_governance", {}) if isinstance(trust.get("permission_governance", {}), dict) else {} + if trust and not permission_governance: + permission_governance = fallback_permission_governance(skill_dir) + if not trust: + permission_status = "warn" + permission_detail = "permission governance evidence is missing because trust report is missing" + elif not permission_governance: + permission_status = "warn" + permission_detail = "permission governance evidence is missing from trust report" + else: + required = int(permission_governance.get("required_count", 0) or 0) + approved = int(permission_governance.get("approval_count", 0) or 0) + gaps = ( + int(permission_governance.get("missing_count", 0) or 0) + + int(permission_governance.get("invalid_count", 0) or 0) + + int(permission_governance.get("expired_count", 0) or 0) + ) + if gaps: + permission_status = "block" if maturity == "governed" else "warn" + else: + permission_status = "pass" + required_names = ", ".join(permission_governance.get("required_capabilities", []) or []) or "none" + permission_detail = f"{approved}/{required} permissions approved; gaps {gaps}; required {required_names}" + gates.append( + gate( + "permission-gates", + "权限批准", + permission_status, + permission_detail, + "reports/security_trust_report.json + security/permission_policy.json", + report_link(output_html, skill_dir, "security/permission_policy.md"), + ) + ) + + runtime_permissions = data["runtime_permissions"] + runtime_permissions_summary = runtime_permissions.get("summary", {}) + if not runtime_permissions: + runtime_permission_status = "block" if maturity == "governed" else "warn" + runtime_permission_detail = "runtime permission probe report is missing" + elif runtime_permissions.get("failures"): + runtime_permission_status = "block" + runtime_permission_detail = f"{runtime_permissions_summary.get('failure_count', len(runtime_permissions.get('failures', [])))} runtime permission probe failures" + else: + runtime_permission_status = "pass" + runtime_permission_detail = ( + f"{runtime_permissions_summary.get('pass_count', 0)}/{runtime_permissions_summary.get('target_count', 0)} targets probed; " + f"native {runtime_permissions_summary.get('native_enforcement_count', 0)}; " + f"metadata fallback {runtime_permissions_summary.get('metadata_fallback_count', 0)}; " + f"installer {runtime_permissions_summary.get('installer_enforcement_pass_count', 0)}; " + f"residual risks {runtime_permissions_summary.get('residual_risk_count', 0)}" + ) + gates.append( + gate( + "permission-runtime", + "权限探针", + runtime_permission_status, + runtime_permission_detail, + "reports/runtime_permission_probes.json", + report_link(output_html, skill_dir, "reports/runtime_permission_probes.md"), + ) + ) + + atlas = data["atlas"] + atlas_summary = atlas.get("summary", {}) + actionable_route_collisions = int( + atlas_summary.get("actionable_route_collision_count", atlas_summary.get("route_collision_count", 0)) or 0 + ) + actionable_owner_gaps = int(atlas_summary.get("actionable_owner_gap_count", atlas_summary.get("owner_gap_count", 0)) or 0) + actionable_stale = int(atlas_summary.get("actionable_stale_count", atlas_summary.get("stale_count", 0)) or 0) + actionable_drift = int(atlas_summary.get("actionable_drift_signal_count", atlas_summary.get("drift_signal_count", 0)) or 0) + atlas_issues = actionable_route_collisions + actionable_owner_gaps + actionable_stale + actionable_drift + 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('actionable_skill_count', atlas_summary.get('skill_count', 0))} actionable; " + f"{actionable_route_collisions} actionable route collisions; " + f"{actionable_owner_gaps} actionable owner gaps; " + f"{actionable_stale} actionable stale; " + f"{actionable_drift} actionable drift; " + f"{atlas_summary.get('non_actionable_issue_count', 0)} scoped non-actionable issues" + ) + gates.append( + gate( + "skill-atlas", + "组合治理", + atlas_status, + atlas_detail, + "reports/skill_atlas.json", + report_link(output_html, skill_dir, "reports/skill_atlas.html"), + ) + ) + + adoption = data["adoption_drift"] + adoption_summary = adoption.get("summary", {}) + daily_skillops = data.get("daily_skillops", {}) + weekly_curator = data.get("weekly_curator", {}) + daily_summary = daily_skillops.get("summary", {}) if isinstance(daily_skillops, dict) else {} + weekly_summary = weekly_curator.get("summary", {}) if isinstance(weekly_curator, dict) else {} + if not adoption: + adoption_status = "warn" + adoption_detail = "adoption drift report is missing; real usage impact is unknown" + elif adoption.get("failures"): + adoption_status = "block" + adoption_detail = f"telemetry privacy or schema failures: {len(adoption.get('failures', []))}" + else: + risk_band = adoption_summary.get("risk_band", "no-data") + adoption_status = "warn" if risk_band in {"no-data", "medium", "high"} else "pass" + adoption_detail = ( + f"{adoption_summary.get('event_count', 0)} metadata events; " + f"adoption {adoption_summary.get('adoption_rate', 0)}; " + f"missed {adoption_summary.get('missed_trigger_count', 0)}; " + f"bad-output {adoption_summary.get('bad_output_count', 0)}; " + f"risk {risk_band}" + ) + skillops_parts = [] + skillops_blocked = False + for label, report, summary in [ + ("daily", daily_skillops, daily_summary), + ("weekly", weekly_curator, weekly_summary), + ]: + if not report: + continue + writes_source = bool(summary.get("writes_source_files") or report.get("writes_source_files")) + auto_patch = bool(summary.get("auto_patch_enabled") or report.get("auto_patch_enabled")) + failure_count = int(summary.get("failure_count", 0) or 0) + if writes_source or auto_patch or failure_count: + skillops_blocked = True + if label == "daily": + skillops_parts.append( + f"daily proposals {summary.get('proposal_count', 0)}; " + f"daily decision {summary.get('decision', report.get('decision', 'unknown'))}; " + f"daily release lock {str(summary.get('release_lock_ready', False)).lower()}" + ) + else: + skillops_parts.append( + f"weekly queue {summary.get('unique_opportunity_count', 0)} unique; " + f"weekly ready {summary.get('ready_for_approval_review_count', 0)}; " + f"weekly top {summary.get('top_score', 0)}; " + f"weekly release lock {str(summary.get('release_lock_ready', False)).lower()}" + ) + if skillops_blocked: + adoption_status = "block" + if skillops_parts: + adoption_detail = adoption_detail + "; " + "; ".join(skillops_parts) + gates.append( + gate( + "operations-loop", + "运营回路", + adoption_status, + adoption_detail, + "reports/adoption_drift_report.json + reports/skillops/daily + reports/skillops/weekly", + report_link(output_html, skill_dir, "reports/adoption_drift_report.md"), + ) + ) + + waiver = data["review_waivers"] + waiver_summary = waiver.get("summary", {}) + active_covered = set(waiver_summary.get("covered_gate_keys", []) or []) + prior_blockers = [item for item in gates if item["status"] == "block"] + prior_warnings = [item for item in gates if item["status"] == "warn"] + unwaived_warnings = [item for item in prior_warnings if item["key"] not in active_covered] + if not waiver: + waiver_status = "warn" + waiver_detail = "review waiver ledger is missing; warning acceptance is not auditable" + elif waiver.get("failures"): + waiver_status = "block" + waiver_detail = f"{len(waiver.get('failures', []))} invalid waiver records" + elif prior_blockers: + waiver_status = "block" + waiver_detail = f"{len(prior_blockers)} blocker gates cannot be waived in v0" + elif unwaived_warnings: + waiver_status = "warn" + waiver_detail = ( + f"{waiver_summary.get('active_count', 0)} active waivers; " + f"{len(unwaived_warnings)} warning gates still need reviewer decision" + ) + else: + waiver_status = "pass" + waiver_detail = f"{waiver_summary.get('active_count', 0)} active waivers cover current warnings" + gates.append( + gate( + "review-waivers", + "人工批准", + waiver_status, + waiver_detail, + "reports/review_waivers.json", + report_link(output_html, skill_dir, "reports/review_waivers.md"), + ) + ) + + world_class_ledger = data.get("world_class_evidence_ledger", {}) + world_class_summary = world_class_ledger.get("summary", {}) if isinstance(world_class_ledger, dict) else {} + source_check_count = int(world_class_summary.get("source_check_count", 0) or 0) + source_pass_count = int(world_class_summary.get("source_pass_count", 0) or 0) + source_blocked_count = int(world_class_summary.get("source_blocked_count", 0) or 0) + source_check_detail = ( + f"source checks {source_pass_count}/{source_check_count} pass; {source_blocked_count} blocked" + if source_check_count + else "source checks unavailable" + ) + if not world_class_ledger and maturity == "governed": + world_class_status = "warn" + world_class_detail = "world-class evidence ledger is missing; public world-class readiness cannot be claimed" + elif not world_class_ledger: + world_class_status = "pass" + world_class_detail = "world-class evidence ledger is optional until governed or public world-class readiness is claimed" + elif world_class_summary.get("ready_to_claim_world_class") is True: + world_class_status = "pass" + world_class_detail = ( + f"{world_class_summary.get('accepted_count', 0)} accepted evidence entries; " + f"{world_class_summary.get('pending_count', 0)} pending; " + f"{source_check_detail}" + ) + else: + world_class_status = "warn" + world_class_detail = ( + f"{world_class_summary.get('pending_count', 0)} pending world-class evidence entries; " + f"{world_class_summary.get('human_pending_count', 0)} human pending; " + f"{world_class_summary.get('external_pending_count', 0)} external pending; " + f"{source_check_detail}; " + f"overclaim guard {str(world_class_summary.get('overclaim_guard_active', False)).lower()}" + ) + gates.append( + gate( + "world-class-evidence", + "世界证据", + world_class_status, + world_class_detail, + "reports/world_class_evidence_ledger.json", + report_link(output_html, skill_dir, "reports/world_class_evidence_ledger.md"), + ) + ) + + registry = data["registry"] + install = data["install_simulation"] + if not registry: + if maturity in {"library", "governed"}: + registry_status = "warn" + registry_detail = "registry audit is missing; package metadata is not reviewable" + else: + registry_status = "pass" + registry_detail = "registry audit is optional until team distribution is required" + else: + compatibility = registry.get("package", {}).get("compatibility", {}) + pass_count = sum(1 for status in compatibility.values() if status == "pass") + registry_status = "block" if registry.get("failures") else ("warn" if registry.get("warnings") else "pass") + registry_detail = ( + f"{registry.get('package', {}).get('name', 'package')} " + f"{registry.get('package', {}).get('version', 'n/a')}; " + f"{pass_count}/{len(compatibility)} compatibility entries pass" + ) + if install: + install_summary = install.get("summary", {}) + permission_failures = int(install_summary.get("installer_permission_failure_count", 0) or 0) + if install.get("failures") or permission_failures: + registry_status = "block" + registry_detail += ( + f"; install {'pass' if install.get('ok') else 'fail'}" + f" with {install_summary.get('adapter_count', 0)} adapters" + f"; installer permissions {install_summary.get('installer_permission_enforced_count', 0)} enforced" + f" / {permission_failures} failures" + ) + gates.append( + gate( + "registry-audit", + "注册审计", + registry_status, + registry_detail, + "reports/registry_audit.json + reports/install_simulation.json", + report_link(output_html, skill_dir, "reports/registry_audit.md"), + ) + ) + + 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" + upgrade = data["upgrade_check"] + if upgrade: + upgrade_summary = upgrade.get("summary", {}) + if upgrade.get("failures"): + release_status = "block" + elif upgrade.get("warnings") and release_status == "pass": + release_status = "warn" + release_detail += ( + f"; upgrade {upgrade_summary.get('declared_bump', 'n/a')}" + f" declared / {upgrade_summary.get('recommended_bump', 'n/a')} recommended" + ) + gates.append( + gate( + "release-notes", + "发布路线", + release_status, + release_detail, + "reports/promotion_decisions.json + reports/upgrade_check.json + docs/migration-v2.md", + report_link(output_html, skill_dir, "reports/promotion_decisions.md") if promotion else str(migration_path), + ) + ) + + return gates diff --git a/scripts/review_studio_layout.py b/scripts/review_studio_layout.py new file mode 100644 index 0000000..95bfe4a --- /dev/null +++ b/scripts/review_studio_layout.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Static layout contract for Review Studio HTML.""" + +import html +from pathlib import Path + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep Review Studio layout and CSS out of gate logic." + + +ROOT = Path(__file__).resolve().parent.parent +ASSET_DIR = ROOT / "assets" + + +REVIEW_STUDIO_NAV = [ + ("#overview", "审查总览"), + ("#intent", "意图画布"), + ("#trigger", "触发实验"), + ("#output", "输出实验"), + ("#actions", "修复动作"), + ("#annotations", "审查批注"), + ("#runtime", "运行矩阵"), + ("#trust", "信任报告"), + ("#permissions", "权限批准"), + ("#permission-probes", "权限探针"), + ("#atlas", "组合治理"), + ("#telemetry", "运营回路"), + ("#waivers", "人工批准"), + ("#world-class", "世界证据"), + ("#registry", "注册审计"), + ("#release", "发布路线"), +] + + +def read_layout_asset(filename: str) -> str: + return (ASSET_DIR / filename).read_text(encoding="utf-8").strip() + + +def render_review_nav(nav_items: list[tuple[str, str]] | None = None) -> str: + items = REVIEW_STUDIO_NAV if nav_items is None else nav_items + return "".join( + f"{html.escape(label)}" + for href, label in items + ) + + +def review_studio_css() -> str: + return read_layout_asset("review-studio.css") diff --git a/scripts/review_studio_output_review.py b/scripts/review_studio_output_review.py new file mode 100644 index 0000000..5e494e4 --- /dev/null +++ b/scripts/review_studio_output_review.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Output review HTML helpers for Review Studio.""" + +import html +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to render output review checklist cards." + + +def render_required_fields(fields: dict[str, Any]) -> str: + if not fields: + return "

    暂无字段要求。

    " + items = [] + for key, value in fields.items(): + items.append(f"
  • {html.escape(str(key))}{html.escape(str(value))}
  • ") + return "
      " + "".join(items) + "
    " + + +def render_command_list(commands: dict[str, Any]) -> str: + if not commands: + return "

    暂无命令。

    " + items = [] + for label, command in commands.items(): + if not command: + continue + items.append( + "
  • " + f"{html.escape(str(label))}" + f"{html.escape(str(command))}" + "
  • " + ) + return "
      " + "".join(items) + "
    " if items else "

    暂无命令。

    " + + +def render_contract(items: list[Any]) -> str: + if not items: + return "

    暂无边界。

    " + return "
      " + "".join(f"
    • {html.escape(str(item))}
    • " for item in items) + "
    " + + +def render_output_review_checklist(adjudication: dict[str, Any]) -> str: + checklist = adjudication.get("reviewer_checklist", []) if isinstance(adjudication, dict) else [] + if not checklist: + return "

    当前没有输出评审操作清单。

    " + cards = [] + for item in checklist: + readiness = str(item.get("readiness", "awaiting-decision")) + answer_key = "可见" if item.get("answer_key_visible") else "隐藏" + cards.append( + "
    " + f"
    {html.escape(readiness)} · 答案{html.escape(answer_key)}" + f"

    {html.escape(str(item.get('case_id', 'case')))}

    " + f"

    {html.escape(str(item.get('blocking_reason', '')))}

    " + f"
    盲评包
    {html.escape(str(item.get('blind_pack_path', '')))}
    " + f"
    决策表
    {html.escape(str(item.get('decisions_path', '')))}
    " + f"
    提示哈希
    {html.escape(str(item.get('prompt_sha256', '')))}
    " + "
    " + "

    操作命令

    " + + render_command_list(item.get("commands", {}) if isinstance(item.get("commands", {}), dict) else {}) + + "
    " + "

    字段要求

    " + + render_required_fields(item.get("required_fields", {}) if isinstance(item.get("required_fields", {}), dict) else {}) + + "
    " + "

    隐私边界

    " + + render_contract(item.get("privacy_contract", []) if isinstance(item.get("privacy_contract", []), list) else []) + + "
    " + "
    " + "
    " + ) + return "
    " + "".join(cards) + "
    " + + +def render_output_review_section(adjudication: dict[str, Any]) -> str: + return ( + "
    " + "

    评审清单

    " + "

    先打开 reports/output_review_kit.md;每张卡片对应一个 blind A/B case,说明当前是否待判、答案是否仍隐藏、应填写的决策文件、有效字段和复跑命令。

    " + f"{render_output_review_checklist(adjudication)}" + "
    " + ) diff --git a/scripts/review_studio_panels.py b/scripts/review_studio_panels.py new file mode 100644 index 0000000..7addbb1 --- /dev/null +++ b/scripts/review_studio_panels.py @@ -0,0 +1,88 @@ +import html +from typing import Any + +from review_studio_gates import status_label + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep small Review Studio panel renderers out of the main page composer." + + +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"
    {html.escape(item['evidence'])} {link_html}
    " + "
    " + ) + 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)}

      {body}
    " + + +def render_review_annotations_panel(annotations_report: dict[str, Any]) -> str: + annotations = annotations_report.get("annotations", []) if isinstance(annotations_report, dict) else [] + if not annotations: + return "

    当前没有 reviewer 批注。

    " + cards = [] + for item in annotations: + line_suffix = f":{item['line']}" if item.get("line") else "" + target_label = f"{item.get('target_path', '')}{line_suffix}" + meta = " · ".join( + part + for part in [ + str(item.get("gate_key", "")), + str(item.get("reviewer", "")), + str(item.get("created_at", "")), + ] + if part + ) + cards.append( + "
    " + f"
    {html.escape(str(item.get('severity', 'note')))} · {html.escape(str(item.get('status', 'open')))}" + f"

    {html.escape(str(item.get('id', 'annotation')))}

    " + f"

    {html.escape(str(item.get('body', '')))}

    " + f"
    位置
    {html.escape(target_label)}
    " + f"
    Gate
    {html.escape(str(item.get('gate_key', '')))}
    " + f"
    建议
    {html.escape(str(item.get('suggested_action', '') or '无'))}
    " + f"{html.escape(meta)}" + f"
    {html.escape(str(item.get('source_excerpt', '')))}
    " + "
    " + ) + return "".join(cards) diff --git a/scripts/review_studio_skillops.py b/scripts/review_studio_skillops.py new file mode 100644 index 0000000..4ebdc22 --- /dev/null +++ b/scripts/review_studio_skillops.py @@ -0,0 +1,48 @@ +from typing import Any + +from review_studio_formatting import render_kv_grid + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep SkillOps summary panels out of the main HTML renderer." + + +def render_skillops_section(data: dict[str, Any]) -> str: + daily_summary = data["daily_skillops"].get("summary", {}) + weekly_summary = data["weekly_curator"].get("summary", {}) + daily_panel = render_kv_grid( + daily_summary, + [ + "decision", + "proposal_count", + "approval_count", + "pending_review_count", + "release_lock_ready", + "public_world_class_ready", + "writes_source_files", + "auto_patch_enabled", + ], + "no daily SkillOps summary", + ) + weekly_panel = render_kv_grid( + weekly_summary, + [ + "decision", + "week_id", + "daily_report_count", + "unique_opportunity_count", + "ready_for_approval_review_count", + "proposal_review_count", + "top_score", + "release_lock_ready", + "writes_source_files", + "auto_patch_enabled", + ], + "no weekly curator summary", + ) + return f""" +
    +

    日常运维

    {daily_panel}
    +

    周度队列

    {weekly_panel}
    +
    +""" diff --git a/scripts/review_studio_waivers.py b/scripts/review_studio_waivers.py new file mode 100644 index 0000000..92d0517 --- /dev/null +++ b/scripts/review_studio_waivers.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Review Studio waiver candidate rendering helpers.""" + +import html +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep waiver candidate layout out of the main renderer." + + +def render_waiver_candidates_panel(review_waivers: dict[str, Any]) -> str: + candidates = review_waivers.get("waiver_candidates", []) if isinstance(review_waivers, dict) else [] + if not candidates: + return "

    当前没有需要 reviewer 处理的批准候选。

    " + cards = [] + for item in candidates: + allowed = bool(item.get("waiver_allowed")) + allowed_label = "可批准" if allowed else "不可批准" + status = str(item.get("status", "unknown")) + required_review = item.get("required_review", []) + required_html = "".join( + f"
  • {html.escape(str(review_item))}
  • " + for review_item in required_review + ) or "
  • 证据不足,需要先补充审查条件。
  • " + decision_options = item.get("decision_options", []) + decision_options_label = ", ".join(str(option) for option in decision_options) if decision_options else "无" + cards.append( + "
    " + f"
    {html.escape(allowed_label)} · {html.escape(status)}" + f"

    {html.escape(str(item.get('label') or item.get('gate_key') or 'Waiver Candidate'))}

    " + f"

    {html.escape(str(item.get('risk_summary') or '证据不足'))}

    " + "
    " + f"
    Gate
    {html.escape(str(item.get('gate_key', '')))}
    " + f"
    证据
    {html.escape(str(item.get('suggested_evidence', '')))}
    " + f"
    选项
    {html.escape(decision_options_label)}
    " + f"
    边界
    {html.escape(str(item.get('world_class_boundary', '')))}
    " + "
    " + "

    审查条件

    " + f"
      {required_html}
    " + "
    建议命令" + f"{html.escape(str(item.get('suggested_command', '')))}
    " + "
    " + ) + return "
    " + "".join(cards) + "
    " diff --git a/scripts/review_studio_world_class.py b/scripts/review_studio_world_class.py new file mode 100644 index 0000000..099e72d --- /dev/null +++ b/scripts/review_studio_world_class.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +"""World-class evidence HTML helpers for Review Studio.""" + +import html +from typing import Any + +from world_class_source_checks import build_source_checklist + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to render world-class evidence cards." + + +def render_inline_list(items: list[Any], empty_label: str) -> str: + if not items: + return f"

    {html.escape(empty_label)}

    " + return "
      " + "".join(f"
    • {html.escape(str(item))}
    • " for item in items) + "
    " + + +def render_source_checks(entry: dict[str, Any]) -> str: + rows = entry.get("source_checklist") + if not isinstance(rows, list): + rows = build_source_checklist([entry]) + if not rows: + return "

    暂无源证据检查。

    " + items = [] + for row in rows: + status = str(row.get("status", "blocked")) + items.append( + "
  • " + f"{html.escape(str(row.get('label', '')))}" + f"{html.escape(str(row.get('field', '')))}: {html.escape(str(row.get('actual', '')))} / {html.escape(str(row.get('expected', '')))}" + f"{html.escape(str(row.get('next_action', '')))}" + "
  • " + ) + return "
      " + "".join(items) + "
    " + + +def render_world_class_evidence_entries(ledger: dict[str, Any]) -> str: + entries = ledger.get("entries", []) if isinstance(ledger, dict) else [] + if not entries: + return "

    当前没有 world-class 证据条目。

    " + cards = [] + for entry in entries: + observed = entry.get("observed_state", {}) if isinstance(entry.get("observed_state", {}), dict) else {} + observed_summary = "; ".join(f"{key}: {value}" for key, value in observed.items()) + submission = entry.get("submission_state", {}) if isinstance(entry.get("submission_state", {}), dict) else {} + submission_summary = "; ".join( + f"{key}: {value}" for key, value in submission.items() if key in {"status", "path", "attested_real_evidence", "privacy_contract_satisfied"} + ) + status = str(entry.get("status", "pending")) + status_label_text = "已接受" if status == "accepted" else "待补证" + cards.append( + "
    " + f"
    {html.escape(status_label_text)} · {html.escape(str(entry.get('category', '')))}" + f"

    {html.escape(str(entry.get('label', entry.get('key', 'evidence'))))}

    " + f"

    {html.escape(str(entry.get('objective', '')))}

    " + f"
    负责人
    {html.escape(str(entry.get('owner', '')))}
    " + f"
    当前状态
    {html.escape(str(entry.get('current', '')))}
    " + f"
    下一步
    {html.escape(str(entry.get('next_action', '')))}
    " + f"
    观测值
    {html.escape(observed_summary or '无')}
    " + f"
    提交态
    {html.escape(submission_summary or 'missing')}
    " + "

    执行步骤

    " + + render_inline_list(entry.get("runbook", []), "暂无执行步骤。") + + "
    " + "
    " + "

    完成定义

    " + + render_inline_list(entry.get("success_checks", []), "暂无完成定义。") + + "
    " + "

    证据来源

    " + + render_inline_list(entry.get("evidence_artifacts", []), "暂无证据来源。") + + "
    " + "

    隐私约束

    " + + render_inline_list(entry.get("privacy_contract", []), "暂无隐私约束。") + + "
    " + "
    " + "

    源证据检查

    " + + render_source_checks(entry) + + "
    " + "
    " + ) + return "
    " + "".join(cards) + "
    " + + +def render_command_list(commands: dict[str, Any]) -> str: + if not commands: + return "

    暂无命令。

    " + items = [] + for label, command in commands.items(): + if not command: + continue + items.append( + "
  • " + f"{html.escape(str(label))}" + f"{html.escape(str(command))}" + "
  • " + ) + return "
      " + "".join(items) + "
    " if items else "

    暂无命令。

    " + + +def render_world_class_intake_checklist(intake: dict[str, Any]) -> str: + items = intake.get("operator_checklist", []) if isinstance(intake, dict) else [] + if not items: + return "

    当前没有 world-class 证据操作清单。

    " + cards = [] + for item in items: + readiness = str(item.get("readiness", "awaiting-submission")) + must_collect = item.get("must_collect", {}) if isinstance(item.get("must_collect", {}), dict) else {} + cards.append( + "
    " + f"
    {html.escape(readiness)} · {html.escape(str(item.get('category', '')))}" + f"

    {html.escape(str(item.get('label', item.get('evidence_key', 'evidence'))))}

    " + f"

    {html.escape(str(item.get('blocking_reason', '')))}

    " + f"
    负责人
    {html.escape(str(item.get('owner', '')))}
    " + f"
    模板
    {html.escape(str(item.get('template_path', '')))}
    " + f"
    提交
    {html.escape(str(item.get('submission_path', '')))}
    " + f"
    下一步
    {html.escape(str(item.get('next_action', '')))}
    " + "
    " + "

    操作命令

    " + + render_command_list(item.get("commands", {}) if isinstance(item.get("commands", {}), dict) else {}) + + "
    " + "

    收集要求

    " + + render_inline_list(must_collect.get("provenance_requirements", []), "暂无来源要求。") + + "
    " + "

    执行步骤

    " + + render_inline_list(must_collect.get("runbook", []), "暂无执行步骤。") + + "
    " + "

    通过条件

    " + + render_inline_list(must_collect.get("success_checks", []), "暂无通过条件。") + + "
    " + "

    隐私边界

    " + + render_inline_list(must_collect.get("privacy_contract", []), "暂无隐私边界。") + + "
    " + "
    " + "
    " + ) + return "
    " + "".join(cards) + "
    " diff --git a/scripts/review_viewer_data.py b/scripts/review_viewer_data.py new file mode 100644 index 0000000..d69f9dc --- /dev/null +++ b/scripts/review_viewer_data.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python3 +"""Data preparation helpers for the compact review viewer.""" + +import json +import re +from pathlib import Path + +from render_intent_confidence import render_intent_confidence +from render_intent_dialogue import render_intent_dialogue +from render_iteration_directions import render_iteration_directions +from render_artifact_design_profile import render_artifact_design_profile +from render_output_risk_profile import render_output_risk_profile +from render_prompt_quality_profile import render_prompt_quality_profile +from render_reference_scan import render_reference_scan +from render_reference_synthesis import render_reference_synthesis +from render_skill_overview import render_skill_overview + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_viewer.py to assemble Review Viewer data before HTML rendering." + + +def load_json(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + +def load_feedback_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "feedback-log.json") + return payload if isinstance(payload, dict) else {} + + +def load_baseline_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "baseline-compare.json") + return payload if isinstance(payload, dict) else {} + + +def load_specific_compare(skill_dir: Path) -> dict: + candidates = [ + skill_dir / "reports" / "description_optimization.json", + skill_dir.parent / "optimization" / "reports" / "description_optimization.json", + ] + for path in candidates: + payload = load_json(path) + if isinstance(payload, dict) and payload: + return payload + return {} + + +def load_specific_promotion(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "promotion_decisions.json") + return payload if isinstance(payload, dict) else {} + + +def load_benchmark_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "github-benchmark-scan.json") + return payload if isinstance(payload, dict) else {} + + +def load_reference_synthesis_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "reference-synthesis.json") + return payload if isinstance(payload, dict) else {} + + +def load_output_risk_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "output-risk-profile.json") + return payload if isinstance(payload, dict) else {} + + +def load_artifact_design_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "artifact-design-profile.json") + return payload if isinstance(payload, dict) else {} + + +def load_prompt_quality_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "prompt-quality-profile.json") + return payload if isinstance(payload, dict) else {} + + +def ensure_report_inputs(skill_dir: Path) -> dict: + overview_json = skill_dir / "reports" / "skill-overview.json" + intent_confidence_json = skill_dir / "reports" / "intent-confidence.json" + intent_json = skill_dir / "reports" / "intent-dialogue.json" + reference_json = skill_dir / "reports" / "reference-scan.json" + reference_synthesis_json = skill_dir / "reports" / "reference-synthesis.json" + output_risk_json = skill_dir / "reports" / "output-risk-profile.json" + artifact_design_json = skill_dir / "reports" / "artifact-design-profile.json" + prompt_quality_json = skill_dir / "reports" / "prompt-quality-profile.json" + directions_json = skill_dir / "reports" / "iteration-directions.json" + + overview_payload = load_json(overview_json) if overview_json.exists() else {} + intent_confidence_payload = load_json(intent_confidence_json) if intent_confidence_json.exists() else {} + intent_payload = load_json(intent_json) if intent_json.exists() else {} + reference_payload = load_json(reference_json) if reference_json.exists() else {} + reference_synthesis_payload = load_json(reference_synthesis_json) if reference_synthesis_json.exists() else {} + output_risk_payload = load_json(output_risk_json) if output_risk_json.exists() else {} + artifact_design_payload = load_json(artifact_design_json) if artifact_design_json.exists() else {} + prompt_quality_payload = load_json(prompt_quality_json) if prompt_quality_json.exists() else {} + directions_payload = load_json(directions_json) if directions_json.exists() else {} + + intent_confidence = intent_confidence_payload or render_intent_confidence(skill_dir)["summary"] + intent = intent_payload or render_intent_dialogue(skill_dir)["summary"] + reference = reference_payload or render_reference_scan(skill_dir, [])["summary"] + reference_synthesis = reference_synthesis_payload or render_reference_synthesis(skill_dir)["summary"] + output_risk = output_risk_payload or render_output_risk_profile(skill_dir)["summary"] + artifact_design = artifact_design_payload or render_artifact_design_profile(skill_dir)["summary"] + prompt_quality = prompt_quality_payload or render_prompt_quality_profile(skill_dir)["summary"] + iteration_payload = directions_payload or render_iteration_directions(skill_dir) + iteration = iteration_payload.get("summary", {}) + overview = overview_payload or render_skill_overview(skill_dir)["summary"] + feedback = load_feedback_summary(skill_dir) + baseline = load_baseline_summary(skill_dir) + compare = load_specific_compare(skill_dir) + promotion = load_specific_promotion(skill_dir) + benchmark = load_benchmark_summary(skill_dir) + reference_synthesis = load_reference_synthesis_summary(skill_dir) + output_risk = load_output_risk_summary(skill_dir) or output_risk + artifact_design = load_artifact_design_summary(skill_dir) or artifact_design + prompt_quality = load_prompt_quality_summary(skill_dir) or prompt_quality + return { + "overview": overview, + "intent_confidence": intent_confidence, + "intent": intent, + "reference": reference, + "iteration": iteration_payload, + "feedback": feedback, + "baseline": baseline, + "compare": compare, + "promotion": promotion, + "benchmark": benchmark, + "reference_synthesis": reference_synthesis, + "output_risk": output_risk, + "artifact_design": artifact_design, + "prompt_quality": prompt_quality, + } + +def architecture_steps(overview: dict) -> list[dict]: + logic = overview.get("logic_steps", [])[:3] + usage = overview.get("usage_steps", [])[:2] + return [ + {"label": "Inputs", "detail": "workflow, prompt, transcript, docs, or notes"}, + {"label": "Boundary", "detail": overview.get("description", "Define the recurring job and exclusions.")}, + {"label": "Logic", "detail": "; ".join(logic) if logic else "Understand, execute, and validate."}, + {"label": "Usage", "detail": "; ".join(usage) if usage else "Load the skill and follow the workflow."}, + {"label": "Next", "detail": "Review the top iteration directions before growing the package."}, + ] + + +def compare_rows(compare: dict) -> list[dict]: + if not compare: + return [] + rows = [] + items = [ + ("Baseline", compare.get("baseline", {})), + ("Current", compare.get("current_candidate", {})), + (compare.get("winner", {}).get("label", "Winner"), compare.get("winner", {})), + ] + for label, payload in items: + if not payload: + continue + dev = payload.get("dev", {}) + holdout = payload.get("holdout", {}) + rows.append( + { + "label": label, + "tokens": payload.get("estimated_tokens", 0), + "dev_errors": dev.get("total_errors", 0), + "holdout_errors": holdout.get("total_errors", 0), + "strategy": payload.get("strategy", "existing"), + } + ) + return rows + + +def benchmark_cards(benchmark: dict) -> list[dict]: + cards = [] + for repo in benchmark.get("repositories", [])[:3]: + cards.append( + { + "name": repo.get("full_name", "Unknown repo"), + "borrow": repo.get("borrow", [])[:2], + "avoid": repo.get("avoid", [])[:1], + } + ) + return cards + + +def synthesis_cards(reference_synthesis: dict) -> list[dict]: + cards = [] + for track in reference_synthesis.get("source_tracks", [])[:3]: + cards.append( + { + "name": track.get("name", "Unknown track"), + "borrow": [track.get("borrow", "")] if track.get("borrow") else [], + "avoid": [track.get("avoid", "")] if track.get("avoid") else [], + } + ) + return cards + + +def split_sentences(text: str) -> list[str]: + if not text: + return [] + parts = [item.strip() for item in re.split(r"(?<=[.!?])\s+", " ".join(text.split())) if item.strip()] + return parts + + +def metric_delta(current: int | float, baseline: int | float) -> str: + delta = current - baseline + if delta == 0: + return "0" + return f"{delta:+}" + + +def variant_diff_cards(compare: dict) -> list[dict]: + baseline = compare.get("baseline", {}) + current = compare.get("current_candidate", {}) + winner = compare.get("winner", {}) + variants = [ + ("Baseline", baseline), + ("Current", current), + (f"Winner — {winner.get('label', 'Winner')}", winner), + ] + baseline_sentences = split_sentences(baseline.get("description", "")) + baseline_set = set(baseline_sentences) + baseline_dev = baseline.get("dev", {}).get("total_errors", 0) + baseline_holdout = baseline.get("holdout", {}).get("total_errors", 0) + cards = [] + seen = set() + for label, payload in variants: + if not payload: + continue + unique_key = (payload.get("description"), payload.get("strategy"), label) + if unique_key in seen: + continue + seen.add(unique_key) + description = payload.get("description", "") + sentences = split_sentences(description) + sentence_set = set(sentences) + added = [item for item in sentences if item not in baseline_set][:3] + removed = [item for item in baseline_sentences if item not in sentence_set][:2] + dev_errors = payload.get("dev", {}).get("total_errors", 0) + holdout_errors = payload.get("holdout", {}).get("total_errors", 0) + cards.append( + { + "label": label, + "strategy": payload.get("strategy", "existing"), + "description": description, + "tokens": payload.get("estimated_tokens", 0), + "dev_errors": dev_errors, + "holdout_errors": holdout_errors, + "token_delta": metric_delta(payload.get("estimated_tokens", 0), baseline.get("estimated_tokens", 0)), + "dev_delta": metric_delta(dev_errors, baseline_dev), + "holdout_delta": metric_delta(holdout_errors, baseline_holdout), + "added": added if label != "Baseline" else baseline_sentences[:3], + "removed": removed, + } + ) + return cards + + +def evidence_readiness(report: dict) -> dict: + intent_confidence = report.get("intent_confidence", {}) + reference_synthesis = report.get("reference_synthesis", {}) + output_risk = report.get("output_risk", {}) + artifact_design = report.get("artifact_design", {}) + prompt_quality = report.get("prompt_quality", {}) + benchmark = report.get("benchmark", {}) + synthesis = reference_synthesis.get("synthesis", {}) if isinstance(reference_synthesis, dict) else {} + pattern_gate = synthesis.get("pattern_gate", {}) if isinstance(synthesis, dict) else {} + accepted_patterns = pattern_gate.get("accepted", []) if isinstance(pattern_gate, dict) else [] + conflicts = synthesis.get("conflicts", []) if isinstance(synthesis, dict) else [] + checks = [ + { + "label": "Intent clarity", + "status": "ready" if intent_confidence.get("gate_passed") else "needs review", + "detail": f"{intent_confidence.get('score', 0)}/100 intent confidence.", + }, + { + "label": "Benchmark coverage", + "status": "ready" if len(benchmark.get("repositories", [])) >= 2 else "needs evidence", + "detail": f"{len(benchmark.get('repositories', []))} GitHub benchmark repositories attached.", + }, + { + "label": "Pattern gate", + "status": "ready" if accepted_patterns else "needs review", + "detail": pattern_gate.get("summary", "No pattern gate summary attached."), + }, + { + "label": "Conflict handling", + "status": "ready" if not conflicts else "decision needed", + "detail": "No material conflicts detected." if not conflicts else conflicts[0].get("summary", "Conflict detected."), + }, + { + "label": "Output risk profile", + "status": "ready" if output_risk.get("risk_families") else "needs review", + "detail": f"{len(output_risk.get('risk_families', []))} output risk families attached.", + }, + { + "label": "Artifact design profile", + "status": "ready" if artifact_design.get("primary_artifact") else "needs review", + "detail": artifact_design.get("primary_artifact", {}).get("direction", "No artifact design profile attached."), + }, + { + "label": "Prompt quality profile", + "status": "ready" if prompt_quality.get("quality_matrix") else "needs review", + "detail": f"{prompt_quality.get('overall_quality_score', 0)}/100 prompt-facing quality score.", + }, + ] + ready_count = sum(1 for item in checks if item["status"] == "ready") + return { + "score": int(ready_count / len(checks) * 100), + "checks": checks, + "reviewer_note": "Use this section to decide whether the package is ready to deepen or should stay in discovery.", + } diff --git a/scripts/run_conformance_suite.py b/scripts/run_conformance_suite.py new file mode 100644 index 0000000..24bdf87 --- /dev/null +++ b/scripts/run_conformance_suite.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + +from skill_ir_paths import find_skill_ir as find_skill_ir_document + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_TARGETS = ["openai", "claude", "agent-skills", "vscode", "generic"] + + +def display_path(path: Path) -> str: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +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) -> tuple[dict[str, Any], str]: + if not path.exists(): + return {}, "" + text = path.read_text(encoding="utf-8") + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" in line: + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def find_skill_ir(skill_dir: Path, name: str) -> dict[str, Any]: + return find_skill_ir_document(skill_dir, name)[0] + + +def add_check(checks: list[str], failures: list[str], condition: bool, passed: str, failed: str) -> None: + if condition: + checks.append(passed) + else: + failures.append(failed) + + +def relative_resource_check(skill_dir: Path, ir: dict[str, Any]) -> tuple[list[str], list[str]]: + checks = [] + failures = [] + resources = ir.get("resources", {}) if isinstance(ir, dict) else {} + for group in ("references", "scripts", "assets", "reports"): + for raw_path in resources.get(group, []): + rel = Path(str(raw_path)) + if rel.is_absolute(): + failures.append(f"{group} resource is absolute: {raw_path}") + continue + target = (skill_dir / rel).resolve() + try: + target.relative_to(skill_dir.resolve()) + except ValueError: + failures.append(f"{group} resource escapes package: {raw_path}") + continue + if not target.exists(): + failures.append(f"{group} resource is missing: {raw_path}") + else: + checks.append(f"{group} resource resolves: {raw_path}") + return checks, failures + + +def common_evidence(skill_dir: Path) -> dict[str, Any]: + frontmatter, _ = parse_frontmatter(skill_dir / "SKILL.md") + manifest = load_json(skill_dir / "manifest.json") + interface_doc = load_yaml(skill_dir / "agents" / "interface.yaml") + name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name) + ir = find_skill_ir(skill_dir, name) + return { + "frontmatter": frontmatter, + "manifest": manifest, + "interface": interface_doc.get("interface", {}), + "compatibility": interface_doc.get("compatibility", {}), + "ir": ir, + "name": name, + "description": str(frontmatter.get("description", "")), + } + + +def target_alias(target: str) -> str: + if target in {"agent-skills", "agentskills"}: + return "agent-skills-compatible" + if target == "vscode": + return "agent-skills-compatible" + return target + + +def check_target(skill_dir: Path, target: str, evidence: dict[str, Any]) -> dict[str, Any]: + checks: list[str] = [] + failures: list[str] = [] + warnings: list[str] = [] + frontmatter = evidence["frontmatter"] + manifest = evidence["manifest"] + interface = evidence["interface"] + compatibility = evidence["compatibility"] + ir = evidence["ir"] + name = evidence["name"] + description = evidence["description"] + + add_check(checks, failures, (skill_dir / "SKILL.md").exists(), "SKILL.md exists", "Missing SKILL.md") + add_check(checks, failures, bool(frontmatter.get("name")), "frontmatter name exists", "Missing frontmatter name") + add_check(checks, failures, bool(description), "frontmatter description exists", "Missing frontmatter description") + add_check(checks, failures, len(description) <= 1024, "description length <= 1024", "description exceeds 1024 characters") + add_check(checks, failures, re.fullmatch(r"[a-z0-9][a-z0-9_-]*", name) is not None, "name is runtime-safe", "name contains unsafe characters") + + if target in {"agent-skills", "vscode"}: + add_check(checks, failures, bool(name), "package identity derives from skill name", "Missing package identity") + if skill_dir.name != name: + warnings.append( + "source checkout directory differs from skill name; package verification must enforce archive top-level identity." + ) + + for field in ("name", "version", "owner", "status", "maturity_tier", "review_cadence"): + add_check(checks, failures, bool(manifest.get(field)), f"manifest.{field} exists", f"Missing manifest.{field}") + targets = [str(item) for item in manifest.get("target_platforms", [])] + alias = target_alias(target) + target_declared = alias in targets or target in targets + target_message = f"manifest declares {target}" if target in targets else f"manifest declares {target} via {alias}" + add_check(checks, failures, target_declared, target_message, f"manifest target missing: {target}") + + for field in ("display_name", "short_description", "default_prompt"): + add_check(checks, failures, bool(interface.get(field)), f"interface.{field} exists", f"Missing interface.{field}") + adapter_targets = [str(item) for item in compatibility.get("adapter_targets", [])] + if target in {"openai", "claude", "generic"}: + add_check(checks, failures, target in adapter_targets, f"adapter target declares {target}", f"adapter target missing: {target}") + else: + add_check(checks, failures, compatibility.get("canonical_format") == "agent-skills", "canonical format is agent-skills", "canonical format must be agent-skills") + + activation = compatibility.get("activation", {}) + execution = compatibility.get("execution", {}) + trust = compatibility.get("trust", {}) + degradation = compatibility.get("degradation", {}) + add_check(checks, failures, bool(activation.get("mode")), "activation mode exists", "Missing activation mode") + add_check(checks, failures, bool(execution.get("context")), "execution context exists", "Missing execution context") + add_check(checks, failures, bool(execution.get("shell")), "execution shell exists", "Missing execution shell") + add_check(checks, failures, bool(trust.get("source_tier")), "trust source tier exists", "Missing trust source tier") + if target in {"openai", "claude", "generic"}: + add_check(checks, failures, bool(degradation.get(target)), f"{target} degradation note exists", f"Missing {target} degradation note") + else: + warnings.append(f"{target} uses canonical Agent Skills metadata; provider-native execution transforms are not implemented in v0.") + + add_check(checks, failures, ir.get("schema_version") == "2.0.0", "Skill IR schema_version is 2.0.0", "Missing or invalid Skill IR") + add_check(checks, failures, ir.get("name") == name, "Skill IR name matches frontmatter", "Skill IR name does not match frontmatter") + add_check( + checks, + failures, + ir.get("trigger_surface", {}).get("description") == description, + "Skill IR description matches frontmatter", + "Skill IR description does not match frontmatter", + ) + resource_checks, resource_failures = relative_resource_check(skill_dir, ir) + checks.extend(resource_checks[:12]) + failures.extend(resource_failures) + + return { + "target": target, + "status": "pass" if not failures else "fail", + "checks": checks, + "failures": failures, + "warnings": warnings, + } + + +def render_markdown(payload: dict[str, Any]) -> str: + lines = [ + "# Runtime Conformance Matrix", + "", + f"- Skill: `{payload['skill']}`", + f"- Targets: `{payload['summary']['target_count']}`", + f"- Passed: `{payload['summary']['pass_count']}`", + f"- Failed: `{payload['summary']['fail_count']}`", + "", + "| Target | Status | Failures | Warnings |", + "| --- | --- | --- | --- |", + ] + for target in payload["targets"]: + failures = "
    ".join(target["failures"]) if target["failures"] else "None" + warnings = "
    ".join(target["warnings"]) if target["warnings"] else "None" + lines.append(f"| {target['target']} | {target['status']} | {failures} | {warnings} |") + lines.extend(["", "## Reviewer Notes", "", "- Failed targets block release for that target.", "- Warnings identify lossy or not-yet-compiled behavior that must remain visible."]) + return "\n".join(lines).strip() + "\n" + + +def run_conformance(skill_dir: Path, targets: list[str], output_json: Path, output_md: Path) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + evidence = common_evidence(skill_dir) + target_results = [check_target(skill_dir, target, evidence) for target in targets] + summary = { + "target_count": len(target_results), + "pass_count": sum(1 for item in target_results if item["status"] == "pass"), + "fail_count": sum(1 for item in target_results if item["status"] == "fail"), + } + payload = { + "ok": summary["fail_count"] == 0, + "skill": evidence["name"], + "targets": target_results, + "summary": summary, + "artifacts": {"json": display_path(output_json), "markdown": display_path(output_md)}, + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run runtime conformance checks for Skill OS 2.0 targets.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--target", action="append", choices=DEFAULT_TARGETS) + parser.add_argument("--output-json") + parser.add_argument("--output-md") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + targets = args.target or DEFAULT_TARGETS + payload = run_conformance( + skill_dir, + targets, + Path(args.output_json).resolve() if args.output_json else skill_dir / "reports" / "conformance_matrix.json", + Path(args.output_md).resolve() if args.output_md else skill_dir / "reports" / "conformance_matrix.md", + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/run_description_optimization_suite.py b/scripts/run_description_optimization_suite.py new file mode 100644 index 0000000..4bfbe0c --- /dev/null +++ b/scripts/run_description_optimization_suite.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path + +from optimize_description import optimize, read_description, render_markdown +from trigger_eval import load_json, load_semantic_config + + +ROOT = Path(__file__).resolve().parent.parent + + +TARGETS = [ + { + "name": "yao-meta-skill", + "title": "Root Description Optimization", + "description_file": ROOT / "SKILL.md", + "baseline_file": ROOT / "evals" / "baseline_description.txt", + "dev_cases": ROOT / "evals" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "evals" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "evals" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "evals" / "adversarial" / "trigger_cases.json", + "semantic_config": ROOT / "evals" / "semantic_config.json", + "output_json": ROOT / "reports" / "description_optimization.json", + "output_md": ROOT / "reports" / "description_optimization.md", + }, + { + "name": "team-frontend-review", + "title": "Frontend Review Description Optimization", + "description_file": ROOT / "examples" / "team-frontend-review" / "generated-skill" / "SKILL.md", + "baseline_file": ROOT / "examples" / "team-frontend-review" / "optimization" / "baseline_description.txt", + "dev_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "adversarial" / "trigger_cases.json", + "semantic_config": ROOT / "examples" / "team-frontend-review" / "optimization" / "semantic_config.json", + "output_json": ROOT / "examples" / "team-frontend-review" / "optimization" / "reports" / "description_optimization.json", + "output_md": ROOT / "examples" / "team-frontend-review" / "optimization" / "reports" / "description_optimization.md", + }, + { + "name": "governed-incident-command", + "title": "Governed Incident Description Optimization", + "description_file": ROOT / "examples" / "governed-incident-command" / "generated-skill" / "SKILL.md", + "baseline_file": ROOT / "examples" / "governed-incident-command" / "optimization" / "baseline_description.txt", + "dev_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "adversarial" / "trigger_cases.json", + "semantic_config": ROOT / "examples" / "governed-incident-command" / "optimization" / "semantic_config.json", + "output_json": ROOT / "examples" / "governed-incident-command" / "optimization" / "reports" / "description_optimization.json", + "output_md": ROOT / "examples" / "governed-incident-command" / "optimization" / "reports" / "description_optimization.md", + }, +] + + +def report_errors(report: dict) -> tuple[int, int]: + if "false_positives" in report and "false_negatives" in report: + return (report["false_positives"], report["false_negatives"]) + return ( + report["holdout"]["false_positives"] if report.get("holdout") else report["dev"]["false_positives"], + report["holdout"]["false_negatives"] if report.get("holdout") else report["dev"]["false_negatives"], + ) + + +def load_existing_snapshots(history_dir: Path, current_output: Path) -> list[dict]: + snapshots = [] + for path in sorted(history_dir.glob("*.json")): + if path == current_output: + continue + snapshots.append(json.loads(path.read_text(encoding="utf-8"))) + return snapshots + + +def target_error_total(target: dict, prefix: str) -> int | None: + fp = target.get(f"{prefix}_fp") + fn = target.get(f"{prefix}_fn") + if fp is None or fn is None: + return None + return fp + fn + + +def calibration_gap(target: dict, gate: str) -> float | None: + calibration = target.get("calibration", {}).get(gate) or {} + return calibration.get("score_gap") + + +def family_gate_note(target: dict, gate: str) -> str: + family = target.get("family_health", {}).get(gate) or {} + if not family: + return "n/a" + weakest = family.get("weakest_family") or {} + weakest_label = weakest.get("family") or "-" + return f"{family.get('clean_family_count', 0)}/{family.get('family_count', 0)} clean; weakest={weakest_label}" + + +def drift_note_for_target(target: dict, previous: dict | None) -> str: + if not previous: + return "initial description optimization snapshot" + + notes = [] + token_delta = target["winner_tokens"] - previous["winner_tokens"] + if token_delta == 0: + notes.append("tokens stable") + else: + notes.append(f"tokens {token_delta:+d}") + + previous_blind = previous.get("winner_blind_holdout_total_errors") + current_blind = target.get("winner_blind_holdout_total_errors") + if previous_blind is None and current_blind is not None: + notes.append(f"blind gate added with {current_blind} errors") + elif previous_blind is not None and current_blind is not None: + delta = current_blind - previous_blind + if delta == 0: + notes.append(f"blind stable at {current_blind}") + else: + notes.append(f"blind error delta {delta:+d}") + + previous_adv = previous.get("winner_adversarial_holdout_total_errors") + current_adv = target.get("winner_adversarial_holdout_total_errors") + if previous_adv is None and current_adv is not None: + notes.append(f"adversarial gate added with {current_adv} errors") + elif previous_adv is not None and current_adv is not None: + delta = current_adv - previous_adv + if delta == 0: + notes.append(f"adversarial stable at {current_adv}") + else: + notes.append(f"adversarial error delta {delta:+d}") + + previous_holdout = target_error_total(previous, "winner_holdout") + current_holdout = target_error_total(target, "winner_holdout") + if previous_holdout is not None and current_holdout is not None: + delta = current_holdout - previous_holdout + if delta == 0: + notes.append(f"holdout stable at {current_holdout}") + else: + notes.append(f"holdout error delta {delta:+d}") + + previous_gap = calibration_gap(previous, "adversarial_holdout") + current_gap = calibration_gap(target, "adversarial_holdout") + if previous_gap is None and current_gap is not None: + notes.append(f"adversarial calibration {current_gap:+.3f}") + elif previous_gap is not None and current_gap is not None: + delta = current_gap - previous_gap + if abs(delta) < 0.001: + notes.append(f"adversarial calibration stable at {current_gap:+.3f}") + else: + notes.append(f"adversarial calibration delta {delta:+.3f}") + + previous_risk = (previous.get("calibration", {}).get("adversarial_holdout") or {}).get("risk_band") + current_risk = (target.get("calibration", {}).get("adversarial_holdout") or {}).get("risk_band") + if previous_risk != current_risk and current_risk: + notes.append(f"risk {previous_risk or 'n/a'} -> {current_risk}") + + return "; ".join(notes) + + +def build_history_snapshot(summary: dict, args: argparse.Namespace) -> dict: + existing_snapshots = load_existing_snapshots(Path(args.history_snapshot_output).parent, Path(args.history_snapshot_output)) + previous_by_target = {} + for snapshot in existing_snapshots: + for target in snapshot.get("targets", []): + previous_by_target[target["name"]] = target + + targets = [] + for target in summary["targets"]: + item = dict(target) + item["drift_note"] = drift_note_for_target(item, previous_by_target.get(item["name"])) + targets.append(item) + + return { + "snapshot_id": args.snapshot_id, + "date": args.snapshot_date, + "commit": args.snapshot_commit, + "label": args.snapshot_label, + "targets": targets, + "notes": [ + "recorded family-level blind, judge-backed blind, and adversarial routing evidence", + "published calibration and drift history for description optimization", + ], + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run description optimization across root and example skills.") + parser.add_argument("--history-snapshot-output") + parser.add_argument("--snapshot-date") + parser.add_argument("--snapshot-id", default="adversarial-calibration-and-family-drift") + parser.add_argument("--snapshot-label", default="Adversarial Calibration And Family Drift") + parser.add_argument("--snapshot-commit", default="local-snapshot") + args = parser.parse_args() + + summary = {"targets": [], "ok": True} + for target in TARGETS: + current_description = read_description(target["description_file"]) + baseline_description = read_description(target["baseline_file"]) + dev_cases = load_json(target["dev_cases"]) + holdout_cases = load_json(target["holdout_cases"]) + blind_holdout_cases = load_json(target["blind_holdout_cases"]) + adversarial_cases = load_json(target["adversarial_cases"]) + config = load_semantic_config(target["semantic_config"]) + + report = optimize( + current_description, + dev_cases, + holdout_cases, + config, + baseline_description, + blind_holdout_cases, + adversarial_cases, + ) + target["output_json"].parent.mkdir(parents=True, exist_ok=True) + target["output_json"].write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + target["output_md"].write_text(render_markdown(report, target["title"]), encoding="utf-8") + + winner_fp, winner_fn = report_errors(report["winner"]) + current_fp, current_fn = report_errors(report["current_candidate"]) + baseline_fp, baseline_fn = report_errors(report["baseline"]) + blind_winner_fp, blind_winner_fn = report_errors(report["acceptance_gates"]["blind_holdout_non_regression"]["winner"]) + blind_current_fp, blind_current_fn = report_errors(report["acceptance_gates"]["blind_holdout_non_regression"]["current"]) + blind_baseline_fp, blind_baseline_fn = report_errors(report["acceptance_gates"]["blind_holdout_non_regression"]["baseline"]) + judge_blind_winner_fp, judge_blind_winner_fn = report_errors( + report["acceptance_gates"]["judge_blind_holdout_non_regression"]["winner"] + ) + judge_blind_current_fp, judge_blind_current_fn = report_errors( + report["acceptance_gates"]["judge_blind_holdout_non_regression"]["current"] + ) + judge_blind_baseline_fp, judge_blind_baseline_fn = report_errors( + report["acceptance_gates"]["judge_blind_holdout_non_regression"]["baseline"] + ) + adversarial_winner_fp, adversarial_winner_fn = report_errors( + report["acceptance_gates"]["adversarial_holdout_non_regression"]["winner"] + ) + adversarial_current_fp, adversarial_current_fn = report_errors( + report["acceptance_gates"]["adversarial_holdout_non_regression"]["current"] + ) + adversarial_baseline_fp, adversarial_baseline_fn = report_errors( + report["acceptance_gates"]["adversarial_holdout_non_regression"]["baseline"] + ) + + target_ok = ( + (winner_fp, winner_fn) <= (current_fp, current_fn) + and (winner_fp, winner_fn) <= (baseline_fp, baseline_fn) + and (blind_winner_fp, blind_winner_fn) <= (blind_current_fp, blind_current_fn) + and (blind_winner_fp, blind_winner_fn) <= (blind_baseline_fp, blind_baseline_fn) + and (judge_blind_winner_fp, judge_blind_winner_fn) <= (judge_blind_current_fp, judge_blind_current_fn) + and (judge_blind_winner_fp, judge_blind_winner_fn) <= (judge_blind_baseline_fp, judge_blind_baseline_fn) + and (adversarial_winner_fp, adversarial_winner_fn) <= (adversarial_current_fp, adversarial_current_fn) + and (adversarial_winner_fp, adversarial_winner_fn) <= (adversarial_baseline_fp, adversarial_baseline_fn) + ) + summary["targets"].append( + { + "name": target["name"], + "winner_label": report["winner"]["label"], + "winner_description": report["winner"]["description"], + "winner_tokens": report["winner"]["estimated_tokens"], + "current_tokens": report["current_candidate"]["estimated_tokens"], + "winner_holdout_fp": winner_fp, + "winner_holdout_fn": winner_fn, + "current_holdout_fp": current_fp, + "current_holdout_fn": current_fn, + "baseline_holdout_fp": baseline_fp, + "baseline_holdout_fn": baseline_fn, + "winner_blind_holdout_fp": blind_winner_fp, + "winner_blind_holdout_fn": blind_winner_fn, + "current_blind_holdout_fp": blind_current_fp, + "current_blind_holdout_fn": blind_current_fn, + "baseline_blind_holdout_fp": blind_baseline_fp, + "baseline_blind_holdout_fn": blind_baseline_fn, + "winner_blind_holdout_total_errors": blind_winner_fp + blind_winner_fn, + "winner_judge_blind_holdout_fp": judge_blind_winner_fp, + "winner_judge_blind_holdout_fn": judge_blind_winner_fn, + "current_judge_blind_holdout_fp": judge_blind_current_fp, + "current_judge_blind_holdout_fn": judge_blind_current_fn, + "baseline_judge_blind_holdout_fp": judge_blind_baseline_fp, + "baseline_judge_blind_holdout_fn": judge_blind_baseline_fn, + "winner_judge_blind_holdout_total_errors": judge_blind_winner_fp + judge_blind_winner_fn, + "winner_adversarial_holdout_fp": adversarial_winner_fp, + "winner_adversarial_holdout_fn": adversarial_winner_fn, + "current_adversarial_holdout_fp": adversarial_current_fp, + "current_adversarial_holdout_fn": adversarial_current_fn, + "baseline_adversarial_holdout_fp": adversarial_baseline_fp, + "baseline_adversarial_holdout_fn": adversarial_baseline_fn, + "winner_adversarial_holdout_total_errors": adversarial_winner_fp + adversarial_winner_fn, + "calibration": { + "holdout": report["acceptance_gates"]["holdout_non_regression"]["winner_calibration"], + "blind_holdout": report["acceptance_gates"]["blind_holdout_non_regression"]["winner_calibration"], + "adversarial_holdout": report["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_calibration"], + }, + "judge_blind": { + "winner": (report["acceptance_gates"]["judge_blind_holdout_non_regression"]["winner"] or {}).get("judge_summary"), + "current": (report["acceptance_gates"]["judge_blind_holdout_non_regression"]["current"] or {}).get("judge_summary"), + "baseline": (report["acceptance_gates"]["judge_blind_holdout_non_regression"]["baseline"] or {}).get("judge_summary"), + }, + "family_health": { + "holdout": report["acceptance_gates"]["holdout_non_regression"]["winner_family_health"], + "blind_holdout": report["acceptance_gates"]["blind_holdout_non_regression"]["winner_family_health"], + "judge_blind_holdout": report["acceptance_gates"]["judge_blind_holdout_non_regression"]["winner_family_health"], + "adversarial_holdout": report["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_family_health"], + }, + "drift_note": "blind, judge-backed blind, adversarial, and calibration gates active", + "ok": target_ok, + } + ) + if not target_ok: + summary["ok"] = False + + rendered = json.dumps(summary, ensure_ascii=False, indent=2) + (ROOT / "reports" / "description_optimization_suite.json").write_text(rendered + "\n", encoding="utf-8") + lines = [ + "# Description Optimization Suite", + "", + "| Target | Winner | Winner Tokens | Holdout FP | Holdout FN | Blind FP | Blind FN | Judge Blind Errors | Adv FP | Adv FN | Adv Gap | Adv Risk | Status |", + "| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |", + ] + for target in summary["targets"]: + lines.append( + f"| `{target['name']}` | `{target['winner_label']}` | {target['winner_tokens']} | {target['winner_holdout_fp']} | {target['winner_holdout_fn']} | {target['winner_blind_holdout_fp']} | {target['winner_blind_holdout_fn']} | {target['winner_judge_blind_holdout_total_errors']} | {target['winner_adversarial_holdout_fp']} | {target['winner_adversarial_holdout_fn']} | {(target['calibration']['adversarial_holdout'] or {}).get('score_gap', '-')} | {(target['calibration']['adversarial_holdout'] or {}).get('risk_band', '-')} | {'ok' if target['ok'] else 'fail'} |" + ) + lines.extend( + [ + "", + "## Family Coverage", + "", + "| Target | Blind Families | Judge Blind Families | Adversarial Families |", + "| --- | --- | --- | --- |", + ] + ) + for target in summary["targets"]: + lines.append( + f"| `{target['name']}` | {family_gate_note(target, 'blind_holdout')} | {family_gate_note(target, 'judge_blind_holdout')} | {family_gate_note(target, 'adversarial_holdout')} |" + ) + (ROOT / "reports" / "description_optimization_suite.md").write_text("\n".join(lines) + "\n", encoding="utf-8") + if args.history_snapshot_output: + snapshot_path = Path(args.history_snapshot_output) + snapshot_path.parent.mkdir(parents=True, exist_ok=True) + snapshot = build_history_snapshot(summary, args) + snapshot_path.write_text(json.dumps(snapshot, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(rendered) + if not summary["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/run_eval_suite.py b/scripts/run_eval_suite.py new file mode 100644 index 0000000..c1bdd76 --- /dev/null +++ b/scripts/run_eval_suite.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +import argparse +import json +import subprocess +import sys +from pathlib import Path + + +def run_case(script: Path, description: Path, baseline: Path, cases: Path) -> dict: + proc = subprocess.run( + [ + sys.executable, + str(script), + "--description-file", + str(description), + "--baseline-description-file", + str(baseline), + "--cases", + str(cases), + "--semantic-config", + str(Path("evals/semantic_config.json").resolve()), + ], + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + payload["returncode"] = proc.returncode + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run semantic trigger evaluation across train/dev/holdout suites.") + parser.add_argument("--eval-dir", default="evals", help="Root eval directory") + parser.add_argument("--description-file", default="evals/improved_description.txt") + parser.add_argument("--baseline-description-file", default="evals/baseline_description.txt") + parser.add_argument("--output-file", help="Optional path to write the combined JSON report") + args = parser.parse_args() + + root = Path(args.eval_dir).resolve() + script = Path("scripts/trigger_eval.py").resolve() + description = Path(args.description_file).resolve() + baseline = Path(args.baseline_description_file).resolve() + + suites = {} + aggregate = {"false_positives": 0, "false_negatives": 0, "precision": [], "recall": []} + family_summary: dict[str, dict] = {} + for name in ("train", "dev", "holdout"): + report = run_case(script, description, baseline, root / name / "trigger_cases.json") + suites[name] = report + aggregate["false_positives"] += report["false_positives"] + aggregate["false_negatives"] += report["false_negatives"] + if report["precision"] is not None: + aggregate["precision"].append(report["precision"]) + if report["recall"] is not None: + aggregate["recall"].append(report["recall"]) + for family, stats in report.get("family_stats", {}).items(): + slot = family_summary.setdefault( + family, + {"total": 0, "passed": 0, "false_positives": 0, "false_negatives": 0}, + ) + slot["total"] += stats["total"] + slot["passed"] += stats["passed"] + slot["false_positives"] += stats["false_positives"] + slot["false_negatives"] += stats["false_negatives"] + + for family, stats in family_summary.items(): + stats["pass_rate"] = round(stats["passed"] / stats["total"], 3) if stats["total"] else None + + total_cases = sum(stats["total"] for stats in family_summary.values()) + summary = { + "suite_count": len(suites), + "total_cases": total_cases, + "family_count": len(family_summary), + "false_positives": aggregate["false_positives"], + "false_negatives": aggregate["false_negatives"], + "average_precision": round(sum(aggregate["precision"]) / len(aggregate["precision"]), 3) if aggregate["precision"] else None, + "average_recall": round(sum(aggregate["recall"]) / len(aggregate["recall"]), 3) if aggregate["recall"] else None, + } + output = {"summary": summary, "family_summary": family_summary, "suites": suites} + rendered = json.dumps(output, ensure_ascii=False, indent=2) + if args.output_file: + Path(args.output_file).write_text(rendered + "\n", encoding="utf-8") + print(rendered) + if summary["false_positives"] > 0 or summary["false_negatives"] > 0: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/run_output_eval.py b/scripts/run_output_eval.py new file mode 100644 index 0000000..8183ceb --- /dev/null +++ b/scripts/run_output_eval.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_CASES = ROOT / "evals" / "output" / "cases.jsonl" +BLIND_SEED = "yao-output-eval-blind-v1" + + +def display_path(path: Path) -> str: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +def load_cases(path: Path) -> list[dict[str, Any]]: + cases = [] + for line_number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1): + stripped = line.strip() + if not stripped: + continue + try: + payload = json.loads(stripped) + except json.JSONDecodeError as exc: + raise ValueError(f"Invalid JSONL at {path}:{line_number}: {exc}") from exc + if not isinstance(payload, dict): + raise ValueError(f"Output eval case at {path}:{line_number} must be an object") + cases.append(payload) + return cases + + +def normalize(text: str) -> str: + return str(text).casefold() + + +def validate_case(case: dict[str, Any], cases_root: Path) -> list[str]: + failures = [] + for key in ("id", "prompt", "baseline_output", "with_skill_output", "assertions"): + if key not in case: + failures.append(f"{case.get('id', '')}: missing {key}") + for raw_path in case.get("input_files", []): + rel = Path(str(raw_path)) + if rel.is_absolute(): + failures.append(f"{case.get('id', '')}: input_files must be relative: {raw_path}") + continue + target = (cases_root / rel).resolve() + try: + target.relative_to(cases_root.resolve()) + except ValueError: + failures.append(f"{case.get('id', '')}: input_file escapes eval folder: {raw_path}") + continue + if not target.exists(): + failures.append(f"{case.get('id', '')}: input_file is missing: {raw_path}") + assertions = case.get("assertions", []) + if not isinstance(assertions, list) or not assertions: + failures.append(f"{case.get('id', '')}: assertions must be a non-empty list") + for assertion in assertions if isinstance(assertions, list) else []: + if not isinstance(assertion, dict): + failures.append(f"{case.get('id', '')}: assertion must be an object") + continue + if not assertion.get("id") or not assertion.get("description"): + failures.append(f"{case.get('id', '')}: assertion id and description are required") + return failures + + +def check_assertion(output: str, assertion: dict[str, Any]) -> dict[str, Any]: + lowered = normalize(output) + required = [str(item) for item in assertion.get("required", [])] + forbidden = [str(item) for item in assertion.get("forbidden", [])] + missing = [item for item in required if normalize(item) not in lowered] + present_forbidden = [item for item in forbidden if normalize(item) in lowered] + passed = not missing and not present_forbidden + return { + "id": assertion.get("id", "assertion"), + "description": assertion.get("description", ""), + "weight": float(assertion.get("weight", 1) or 0), + "failure_type": assertion.get("failure_type", "assertion_failed"), + "passed": passed, + "missing": missing, + "present_forbidden": present_forbidden, + } + + +def grade_output(output: str, assertions: list[dict[str, Any]]) -> dict[str, Any]: + checks = [check_assertion(output, assertion) for assertion in assertions] + total_weight = sum(item["weight"] for item in checks) or len(checks) or 1 + passed_weight = sum(item["weight"] for item in checks if item["passed"]) + failed = [item for item in checks if not item["passed"]] + return { + "score": round(passed_weight / total_weight * 100, 2), + "passed_count": len(checks) - len(failed), + "failed_count": len(failed), + "checks": checks, + "failed": failed, + } + + +def grade_case(case: dict[str, Any]) -> dict[str, Any]: + assertions = case.get("assertions", []) + baseline = grade_output(str(case.get("baseline_output", "")), assertions) + with_skill = grade_output(str(case.get("with_skill_output", "")), assertions) + return { + "id": case["id"], + "prompt": case["prompt"], + "input_files": case.get("input_files", []), + "metadata": case.get("metadata", {}), + "baseline": baseline, + "with_skill": with_skill, + "delta": round(with_skill["score"] - baseline["score"], 2), + "winner": "with_skill" if with_skill["score"] >= baseline["score"] else "baseline", + "failure_taxonomy": sorted({item["failure_type"] for item in with_skill["failed"]}), + } + + +def blind_variant_order(case_id: str) -> list[str]: + digest = hashlib.sha256(f"{BLIND_SEED}:{case_id}".encode("utf-8")).hexdigest() + return ["baseline", "with_skill"] if int(digest[:2], 16) % 2 == 0 else ["with_skill", "baseline"] + + +def output_for_role(case: dict[str, Any], role: str) -> str: + return str(case.get("baseline_output" if role == "baseline" else "with_skill_output", "")) + + +def expected_role(case: dict[str, Any]) -> str: + review = case.get("human_review", {}) if isinstance(case.get("human_review"), dict) else {} + winner = str(review.get("expected_winner", "with_skill")) + return winner if winner in {"baseline", "with_skill"} else "with_skill" + + +def build_blind_review_pack(cases: list[dict[str, Any]], results: list[dict[str, Any]]) -> tuple[dict[str, Any], dict[str, Any]]: + result_by_id = {item["id"]: item for item in results} + pairs = [] + answer_pairs = [] + for case in cases: + case_id = str(case["id"]) + order = blind_variant_order(case_id) + variant_a_role, variant_b_role = order + expected = expected_role(case) + expected_variant = "A" if variant_a_role == expected else "B" + assertions = case.get("assertions", []) if isinstance(case.get("assertions"), list) else [] + rubric = [ + { + "id": str(item.get("id", "assertion")), + "description": str(item.get("description", "")), + "weight": float(item.get("weight", 1) or 0), + } + for item in assertions + if isinstance(item, dict) + ] + pairs.append( + { + "case_id": case_id, + "prompt": str(case.get("prompt", "")), + "input_files": case.get("input_files", []), + "metadata": case.get("metadata", {}), + "review_instruction": "Pick A or B based only on the rubric. Do not infer which output came from the skill.", + "rubric": rubric, + "variant_a": { + "blind_id": f"{case_id}:A", + "output": output_for_role(case, variant_a_role), + }, + "variant_b": { + "blind_id": f"{case_id}:B", + "output": output_for_role(case, variant_b_role), + }, + } + ) + scored = result_by_id.get(case_id, {}) + answer_pairs.append( + { + "case_id": case_id, + "variant_a_role": variant_a_role, + "variant_b_role": variant_b_role, + "expected_winner_role": expected, + "expected_winner_variant": expected_variant, + "score_winner_role": scored.get("winner", ""), + "delta": scored.get("delta", 0), + } + ) + pack = { + "schema_version": "1.0", + "seed": BLIND_SEED, + "summary": { + "pair_count": len(pairs), + "answer_key_separate": True, + "with_skill_hidden_count": sum( + 1 + for pair in answer_pairs + if pair["variant_a_role"] == "with_skill" or pair["variant_b_role"] == "with_skill" + ), + }, + "pairs": pairs, + } + answer_key = { + "schema_version": "1.0", + "seed": BLIND_SEED, + "summary": { + "pair_count": len(answer_pairs), + "with_skill_expected_count": sum(1 for pair in answer_pairs if pair["expected_winner_role"] == "with_skill"), + "baseline_expected_count": sum(1 for pair in answer_pairs if pair["expected_winner_role"] == "baseline"), + }, + "answers": answer_pairs, + } + return pack, answer_key + + +def build_summary(results: list[dict[str, Any]]) -> dict[str, Any]: + case_count = len(results) + baseline_average = sum(item["baseline"]["score"] for item in results) / case_count if case_count else 0 + with_skill_average = sum(item["with_skill"]["score"] for item in results) / case_count if case_count else 0 + regressions = [item for item in results if item["delta"] < 0] + failures = sorted({failure for item in results for failure in item["failure_taxonomy"]}) + file_backed = [item for item in results if item.get("input_files")] + near_neighbors = [item for item in results if item.get("metadata", {}).get("case_type") == "near_neighbor"] + boundary_cases = [item for item in results if item.get("metadata", {}).get("case_type") == "boundary"] + return { + "case_count": case_count, + "file_backed_case_count": len(file_backed), + "near_neighbor_case_count": len(near_neighbors), + "boundary_case_count": len(boundary_cases), + "baseline_pass_rate": round(baseline_average, 2), + "with_skill_pass_rate": round(with_skill_average, 2), + "delta": round(with_skill_average - baseline_average, 2), + "regression_count": len(regressions), + "gate_pass": with_skill_average >= baseline_average and not regressions, + "failure_taxonomy": failures, + } + + +def render_markdown(payload: dict[str, Any]) -> str: + summary = payload["summary"] + lines = [ + "# Output Quality Scorecard", + "", + "This v0 scorecard compares static without-skill and with-skill outputs using assertion grading.", + "", + f"- Cases: `{summary['case_count']}`", + f"- Baseline pass rate: `{summary['baseline_pass_rate']}`", + f"- With-skill pass rate: `{summary['with_skill_pass_rate']}`", + f"- Delta: `{summary['delta']}`", + f"- Regressions: `{summary['regression_count']}`", + f"- Blind A/B pairs: `{summary.get('blind_pair_count', 0)}`", + f"- Gate pass: `{summary['gate_pass']}`", + "", + "Blind review artifacts are generated separately so reviewers can inspect A/B outputs without seeing the answer key.", + "Run output review adjudication after reviewer decisions are recorded; pending cases should stay pending rather than being counted as human agreement.", + "", + "## Case Results", + "", + "| Case | Baseline | With Skill | Delta | Winner | Failed With-Skill Assertions |", + "| --- | ---: | ---: | ---: | --- | --- |", + ] + for item in payload["results"]: + failed = ", ".join(failure["id"] for failure in item["with_skill"]["failed"]) or "None" + lines.append( + f"| {item['id']} | {item['baseline']['score']} | {item['with_skill']['score']} | {item['delta']} | {item['winner']} | {failed} |" + ) + lines.extend(["", "## Failure Taxonomy", ""]) + if summary["failure_taxonomy"]: + for failure in summary["failure_taxonomy"]: + lines.append(f"- {failure}") + else: + lines.append("- No with-skill assertion failures.") + lines.extend( + [ + "", + "## Next Fixes", + "", + "- Add holdout cases before using this as a release gate.", + "- Promote repeated failed assertions into the output-risk profile.", + "- Keep assertions tied to material deliverables, not phrasing trivia.", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def render_blind_review_markdown(pack: dict[str, Any]) -> str: + summary = pack["summary"] + lines = [ + "# Output Blind A/B Review Pack", + "", + "This packet hides whether each variant came from the baseline or the skill-guided output. Use the separate answer key only after review.", + "", + f"- Pairs: `{summary['pair_count']}`", + f"- Seed: `{pack['seed']}`", + f"- Answer key separate: `{summary['answer_key_separate']}`", + "", + ] + for pair in pack["pairs"]: + lines.extend( + [ + f"## Case: {pair['case_id']}", + "", + f"Prompt: {pair['prompt']}", + "", + "Rubric:", + ] + ) + for item in pair["rubric"]: + lines.append(f"- `{item['id']}` ({item['weight']}): {item['description']}") + lines.extend( + [ + "", + "### Variant A", + "", + str(pair["variant_a"]["output"]), + "", + "### Variant B", + "", + str(pair["variant_b"]["output"]), + "", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def run_output_eval( + cases_path: Path, + output_json: Path, + output_md: Path, + blind_pack_json: Path, + blind_pack_md: Path, + blind_answer_key_json: Path, +) -> dict[str, Any]: + cases = load_cases(cases_path) + validation_failures = [failure for case in cases for failure in validate_case(case, cases_path.parent)] + if validation_failures: + blind_pack = { + "schema_version": "1.0", + "seed": BLIND_SEED, + "summary": {"pair_count": 0, "answer_key_separate": True, "with_skill_hidden_count": 0}, + "pairs": [], + } + blind_answer_key = { + "schema_version": "1.0", + "seed": BLIND_SEED, + "summary": {"pair_count": 0, "with_skill_expected_count": 0, "baseline_expected_count": 0}, + "answers": [], + } + payload = { + "ok": False, + "cases": display_path(cases_path), + "summary": { + "case_count": len(cases), + "baseline_pass_rate": 0, + "with_skill_pass_rate": 0, + "delta": 0, + "regression_count": 0, + "gate_pass": False, + "blind_pair_count": 0, + "failure_taxonomy": ["invalid_case"], + }, + "results": [], + "failures": validation_failures, + } + else: + results = [grade_case(case) for case in cases] + blind_pack, blind_answer_key = build_blind_review_pack(cases, results) + payload = { + "ok": True, + "cases": display_path(cases_path), + "summary": build_summary(results), + "results": results, + "failures": [], + } + payload["summary"]["blind_pair_count"] = blind_pack["summary"]["pair_count"] + payload["blind_review"] = { + "pack": display_path(blind_pack_json), + "answer_key": display_path(blind_answer_key_json), + "pair_count": blind_pack["summary"]["pair_count"], + } + payload["artifacts"] = { + "json": display_path(output_json), + "markdown": display_path(output_md), + "blind_review_pack_json": display_path(blind_pack_json), + "blind_review_pack_md": display_path(blind_pack_md), + "blind_answer_key_json": display_path(blind_answer_key_json), + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + blind_pack_json.parent.mkdir(parents=True, exist_ok=True) + blind_pack_md.parent.mkdir(parents=True, exist_ok=True) + blind_answer_key_json.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + blind_pack_json.write_text(json.dumps(blind_pack, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + blind_pack_md.write_text(render_blind_review_markdown(blind_pack), encoding="utf-8") + blind_answer_key_json.write_text(json.dumps(blind_answer_key, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run Output Eval Lab assertion grading for with-skill vs baseline outputs.") + parser.add_argument("--cases", default=str(DEFAULT_CASES)) + parser.add_argument("--output-json", default=str(ROOT / "reports" / "output_quality_scorecard.json")) + parser.add_argument("--output-md", default=str(ROOT / "reports" / "output_quality_scorecard.md")) + parser.add_argument("--blind-pack-json", default=str(ROOT / "reports" / "output_blind_review_pack.json")) + parser.add_argument("--blind-pack-md", default=str(ROOT / "reports" / "output_blind_review_pack.md")) + parser.add_argument("--blind-answer-key-json", default=str(ROOT / "reports" / "output_blind_answer_key.json")) + args = parser.parse_args() + + payload = run_output_eval( + Path(args.cases).resolve(), + Path(args.output_json).resolve(), + Path(args.output_md).resolve(), + Path(args.blind_pack_json).resolve(), + Path(args.blind_pack_md).resolve(), + Path(args.blind_answer_key_json).resolve(), + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/run_output_execution.py b/scripts/run_output_execution.py new file mode 100644 index 0000000..0e62368 --- /dev/null +++ b/scripts/run_output_execution.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import shlex +import subprocess +import time +from pathlib import Path +from typing import Any + +from run_output_eval import DEFAULT_CASES, display_path, grade_output, load_cases, validate_case + + +ROOT = Path(__file__).resolve().parent.parent +DEFAULT_OUTPUT_JSON = ROOT / "reports" / "output_execution_runs.json" +DEFAULT_OUTPUT_MD = ROOT / "reports" / "output_execution_runs.md" +VARIANTS = ("baseline", "with_skill") + + +def output_for_variant(case: dict[str, Any], variant: str) -> str: + return str(case.get("baseline_output" if variant == "baseline" else "with_skill_output", "")) + + +def output_key(variant: str) -> str: + return "baseline_output" if variant == "baseline" else "with_skill_output" + + +def parse_runner_command(value: str | None) -> list[str]: + if not value: + return [] + stripped = value.strip() + if stripped.startswith("["): + payload = json.loads(stripped) + if not isinstance(payload, list) or not all(isinstance(item, str) and item for item in payload): + raise ValueError("--runner-command JSON must be a non-empty string list") + return payload + return shlex.split(stripped) + + +def display_command(command: list[str]) -> list[str]: + displayed: list[str] = [] + for item in command: + path = Path(item) + if path.is_absolute() and path.exists(): + displayed.append(display_path(path)) + else: + displayed.append(item) + return displayed + + +def estimate_tokens(text: str) -> int: + if not text: + return 0 + return max(1, round(len(text) / 4)) + + +def parse_runner_stdout(stdout: str) -> tuple[str, dict[str, Any]]: + stripped = stdout.strip() + if not stripped: + return "", {} + try: + payload = json.loads(stripped) + except json.JSONDecodeError: + return stdout, {} + if not isinstance(payload, dict): + return stdout, {} + return str(payload.get("output", "")), payload + + +def usage_payload(payload: dict[str, Any], prompt: str, output: str) -> dict[str, Any]: + usage = payload.get("usage", {}) if isinstance(payload.get("usage"), dict) else {} + input_tokens = usage.get("input_tokens", usage.get("prompt_tokens")) + output_tokens = usage.get("output_tokens", usage.get("completion_tokens")) + total_tokens = usage.get("total_tokens") + estimated = bool(usage.get("estimated", False)) + if input_tokens is None: + input_tokens = estimate_tokens(prompt) + estimated = True + if output_tokens is None: + output_tokens = estimate_tokens(output) + estimated = True + if total_tokens is None: + total_tokens = int(input_tokens or 0) + int(output_tokens or 0) + estimated = True + return { + "input_tokens": int(input_tokens or 0), + "output_tokens": int(output_tokens or 0), + "total_tokens": int(total_tokens or 0), + "estimated": estimated, + } + + +def recorded_fixture_run(case: dict[str, Any], variant: str, assertions: list[dict[str, Any]]) -> dict[str, Any]: + output = output_for_variant(case, variant) + grade = grade_output(output, assertions) + return { + "case_id": str(case.get("id", "")), + "variant": variant, + "status": "pass", + "execution_mode": "recorded_fixture", + "model_executed": False, + "command_executed": False, + "duration_ms": None, + "provider": "", + "model": "", + "usage": usage_payload({}, str(case.get("prompt", "")), output), + "score": grade["score"], + "passed_count": grade["passed_count"], + "failed_count": grade["failed_count"], + "failed_assertions": [item["id"] for item in grade["failed"]], + "output_sha256": hashlib.sha256(output.encode("utf-8")).hexdigest(), + "failure": "", + } + + +def command_run( + case: dict[str, Any], + variant: str, + assertions: list[dict[str, Any]], + command: list[str], + timeout_seconds: float, +) -> dict[str, Any]: + request = { + "case_id": str(case.get("id", "")), + "variant": variant, + "prompt": str(case.get("prompt", "")), + "input_files": case.get("input_files", []), + "metadata": case.get("metadata", {}), + "fixture_output": output_for_variant(case, variant), + "output_key": output_key(variant), + } + started = time.perf_counter() + try: + proc = subprocess.run( + command, + input=json.dumps(request, ensure_ascii=False), + capture_output=True, + text=True, + timeout=timeout_seconds, + check=False, + ) + duration_ms = round((time.perf_counter() - started) * 1000, 2) + except subprocess.TimeoutExpired as exc: + return { + "case_id": request["case_id"], + "variant": variant, + "status": "fail", + "execution_mode": "command", + "model_executed": False, + "command_executed": True, + "duration_ms": round(timeout_seconds * 1000, 2), + "provider": "", + "model": "", + "usage": usage_payload({}, request["prompt"], ""), + "score": 0, + "passed_count": 0, + "failed_count": len(assertions), + "failed_assertions": [str(item.get("id", "assertion")) for item in assertions], + "output_sha256": "", + "failure": f"runner timed out after {timeout_seconds}s: {exc}", + } + output, payload = parse_runner_stdout(proc.stdout) + grade = grade_output(output, assertions) + execution_kind = str(payload.get("execution_kind", "command")) + provider = str(payload.get("provider", "")) + model = str(payload.get("model", "")) + model_executed = execution_kind == "model" and bool(model and provider) + return { + "case_id": request["case_id"], + "variant": variant, + "status": "pass" if proc.returncode == 0 and output else "fail", + "execution_mode": execution_kind if execution_kind in {"command", "model"} else "command", + "model_executed": model_executed, + "command_executed": True, + "duration_ms": duration_ms, + "provider": provider, + "model": model, + "usage": usage_payload(payload, request["prompt"], output), + "score": grade["score"] if output else 0, + "passed_count": grade["passed_count"] if output else 0, + "failed_count": grade["failed_count"] if output else len(assertions), + "failed_assertions": [item["id"] for item in grade["failed"]] if output else [str(item.get("id", "assertion")) for item in assertions], + "output_sha256": hashlib.sha256(output.encode("utf-8")).hexdigest() if output else "", + "failure": "" if proc.returncode == 0 and output else (proc.stderr.strip() or "runner returned no output"), + } + + +def build_summary(runs: list[dict[str, Any]]) -> dict[str, Any]: + variant_run_count = len(runs) + case_ids = sorted({item["case_id"] for item in runs}) + baseline = [item for item in runs if item["variant"] == "baseline"] + with_skill = [item for item in runs if item["variant"] == "with_skill"] + baseline_average = sum(item["score"] for item in baseline) / len(baseline) if baseline else 0 + with_skill_average = sum(item["score"] for item in with_skill) / len(with_skill) if with_skill else 0 + regression_count = sum( + 1 + for case_id in case_ids + if max((item["score"] for item in with_skill if item["case_id"] == case_id), default=0) + < max((item["score"] for item in baseline if item["case_id"] == case_id), default=0) + ) + failure_count = sum(1 for item in runs if item["status"] != "pass") + command_executed_count = sum(1 for item in runs if item.get("command_executed")) + model_executed_count = sum(1 for item in runs if item.get("model_executed")) + recorded_fixture_count = sum(1 for item in runs if item.get("execution_mode") == "recorded_fixture") + timing_observed_count = sum(1 for item in runs if item.get("duration_ms") is not None) + token_estimated_count = sum(1 for item in runs if item.get("usage", {}).get("estimated")) + token_observed_count = variant_run_count - token_estimated_count + return { + "case_count": len(case_ids), + "variant_run_count": variant_run_count, + "command_executed_count": command_executed_count, + "model_executed_count": model_executed_count, + "recorded_fixture_count": recorded_fixture_count, + "timing_observed_count": timing_observed_count, + "token_observed_count": token_observed_count, + "token_estimated_count": token_estimated_count, + "baseline_pass_rate": round(baseline_average, 2), + "with_skill_pass_rate": round(with_skill_average, 2), + "delta": round(with_skill_average - baseline_average, 2), + "regression_count": regression_count, + "failure_count": failure_count, + "gate_pass": failure_count == 0 and with_skill_average >= baseline_average and regression_count == 0, + } + + +def render_markdown(payload: dict[str, Any]) -> str: + summary = payload["summary"] + lines = [ + "# Output Execution Runs", + "", + "This report records how output-eval variants were produced and whether timing or token evidence is observed or estimated.", + "", + f"- Cases: `{summary['case_count']}`", + f"- Variant runs: `{summary['variant_run_count']}`", + f"- Command executed: `{summary['command_executed_count']}`", + f"- Model executed: `{summary['model_executed_count']}`", + f"- Recorded fixtures: `{summary['recorded_fixture_count']}`", + f"- Timing observed: `{summary['timing_observed_count']}`", + f"- Token observed: `{summary['token_observed_count']}`", + f"- Token estimated: `{summary['token_estimated_count']}`", + f"- Delta: `{summary['delta']}`", + f"- Gate pass: `{summary['gate_pass']}`", + "", + ] + if summary["model_executed_count"] == 0: + lines.extend( + [ + "No model-executed runs are recorded yet.", + "", + "Use `python3 scripts/yao.py output-exec --provider-runner openai` or `--runner-command` with a reviewed provider-backed runner to replace recorded fixtures with real model output evidence.", + "", + ] + ) + if summary["command_executed_count"] > 0: + lines.extend( + [ + "Command runner evidence is present. This proves the eval harness executed an external command, but it is not provider-backed model evidence unless the runner reports model metadata.", + "", + ] + ) + lines.extend( + [ + "## Runs", + "", + "| Case | Variant | Mode | Model | Duration ms | Tokens | Score | Status |", + "| --- | --- | --- | --- | ---: | ---: | ---: | --- |", + ] + ) + for item in payload["runs"]: + usage = item.get("usage", {}) + duration = "" if item.get("duration_ms") is None else str(item["duration_ms"]) + model = item.get("model") or item.get("provider") or "" + lines.append( + f"| {item['case_id']} | {item['variant']} | {item['execution_mode']} | {model} | " + f"{duration} | {usage.get('total_tokens', 0)} | {item['score']} | {item['status']} |" + ) + failures = [item for item in payload["runs"] if item.get("failure")] + if failures: + lines.extend(["", "## Failures", ""]) + for item in failures: + lines.append(f"- `{item['case_id']}` `{item['variant']}`: {item['failure']}") + lines.extend( + [ + "", + "## Next Fixes", + "", + "- Keep recorded fixtures as reproducible baselines, but do not describe them as model-executed evidence.", + "- Use `scripts/provider_output_eval_runner.py` for provider-backed holdout cases when release confidence depends on real generation behavior.", + "- Compare timing, token cost, and assertion deltas before promoting a skill to governed reuse.", + ] + ) + return "\n".join(lines).strip() + "\n" + + +def run_output_execution( + cases_path: Path, + output_json: Path, + output_md: Path, + runner_command: list[str], + timeout_seconds: float, +) -> dict[str, Any]: + cases = load_cases(cases_path) + validation_failures = [failure for case in cases for failure in validate_case(case, cases_path.parent)] + runs: list[dict[str, Any]] = [] + if not validation_failures: + for case in cases: + assertions = case.get("assertions", []) if isinstance(case.get("assertions"), list) else [] + for variant in VARIANTS: + if runner_command: + runs.append(command_run(case, variant, assertions, runner_command, timeout_seconds)) + else: + runs.append(recorded_fixture_run(case, variant, assertions)) + summary = build_summary(runs) + failures = validation_failures + [f"{item['case_id']} {item['variant']}: {item['failure']}" for item in runs if item.get("failure")] + payload = { + "schema_version": "1.0", + "ok": not failures and summary["gate_pass"], + "cases": display_path(cases_path), + "runner": { + "mode": "command" if runner_command else "recorded_fixture", + "command": display_command(runner_command), + "timeout_seconds": timeout_seconds if runner_command else None, + }, + "summary": summary, + "runs": runs, + "failures": failures, + "artifacts": { + "json": display_path(output_json), + "markdown": display_path(output_md), + }, + } + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="Record output-eval execution evidence for static, command, or model-backed runs.") + parser.add_argument("--cases", default=str(DEFAULT_CASES)) + parser.add_argument("--output-json", default=str(DEFAULT_OUTPUT_JSON)) + parser.add_argument("--output-md", default=str(DEFAULT_OUTPUT_MD)) + parser.add_argument("--runner-command", help="Command string or JSON string list. Receives a JSON request on stdin.") + parser.add_argument("--timeout-seconds", type=float, default=30.0) + args = parser.parse_args() + + try: + runner_command = parse_runner_command(args.runner_command) + except (json.JSONDecodeError, ValueError) as exc: + payload = { + "schema_version": "1.0", + "ok": False, + "cases": display_path(Path(args.cases).resolve()), + "runner": {"mode": "invalid", "command": [], "timeout_seconds": args.timeout_seconds}, + "summary": build_summary([]), + "runs": [], + "failures": [str(exc)], + "artifacts": {"json": display_path(Path(args.output_json).resolve()), "markdown": display_path(Path(args.output_md).resolve())}, + } + output_json = Path(args.output_json).resolve() + output_md = Path(args.output_md).resolve() + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(2) + + payload = run_output_execution( + Path(args.cases).resolve(), + Path(args.output_json).resolve(), + Path(args.output_md).resolve(), + runner_command, + args.timeout_seconds, + ) + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/simulate_install.py b/scripts/simulate_install.py new file mode 100644 index 0000000..4dc0074 --- /dev/null +++ b/scripts/simulate_install.py @@ -0,0 +1,392 @@ +#!/usr/bin/env python3 +import argparse +import json +import shutil +import tempfile +import zipfile +from datetime import date, datetime +from pathlib import Path, PurePosixPath +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +ROOT = Path(__file__).resolve().parent.parent + + +def display_path(path: Path, root: Path = ROOT) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +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 read_frontmatter(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + text = path.read_text(encoding="utf-8", errors="replace") + if not text.startswith("---"): + return {} + parts = text.split("---", 2) + if len(parts) < 3: + return {} + if yaml is not None: + payload = yaml.safe_load(parts[1]) or {} + return payload if isinstance(payload, dict) else {} + data: dict[str, Any] = {} + for line in parts[1].splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data + + +def zip_names(zip_path: Path) -> list[str]: + with zipfile.ZipFile(zip_path) as archive: + return archive.namelist() + + +def unsafe_zip_entries(names: list[str]) -> list[str]: + unsafe = [] + for name in names: + pure = PurePosixPath(name) + if pure.is_absolute() or ".." in pure.parts or name.startswith("\\") or (pure.parts and ":" in pure.parts[0]): + unsafe.append(name) + return unsafe + + +def top_level_dirs(names: list[str]) -> list[str]: + roots = set() + for name in names: + parts = PurePosixPath(name).parts + if parts: + roots.add(parts[0]) + return sorted(roots) + + +def non_root_skill_entries(names: list[str], package_root: str) -> list[str]: + root_entry = f"{package_root}/SKILL.md" + return sorted(name for name in names if PurePosixPath(name).name == "SKILL.md" and name != root_entry) + + +def add_check(checks: list[dict[str, str]], failures: list[str], check_id: str, passed: bool, detail: str) -> None: + checks.append({"id": check_id, "status": "pass" if passed else "fail", "detail": detail}) + if not passed: + failures.append(detail) + + +def package_name(package_manifest: dict[str, Any], skill_dir: Path) -> str: + return str(package_manifest.get("name") or skill_dir.name) + + +def adapter_targets(adapter_root: Path) -> list[str]: + targets_dir = adapter_root / "targets" + if not targets_dir.exists(): + return [] + return sorted(path.name for path in targets_dir.iterdir() if path.is_dir()) + + +def sorted_strings(value: Any) -> list[str]: + return sorted(str(item) for item in value) if isinstance(value, list) else [] + + +def parse_date(value: str) -> date | None: + try: + return datetime.strptime(value, "%Y-%m-%d").date() + except ValueError: + return None + + +def permission_policy_checks( + installed_dir: Path | None, + adapter_root: Path, + targets: list[str], + generated_at: str, + checks: list[dict[str, str]], + failures: list[str], +) -> dict[str, int]: + installed = installed_dir is not None and installed_dir.exists() and installed_dir.is_dir() + policy = load_json(installed_dir / "security" / "permission_policy.json") if installed else {} + capabilities = policy.get("capabilities", {}) if isinstance(policy.get("capabilities"), dict) else {} + generated_date = parse_date(generated_at[:10]) + enforced_count = 0 + permission_failure_start = len(failures) + declared_capabilities: set[str] = set() + + add_check(checks, failures, "permission-policy-load", bool(capabilities), "Installed permission policy is readable") + for target in targets: + adapter = load_json(adapter_root / "targets" / target / "adapter.json") + target_contract = adapter.get("target_permission_contract", {}) if isinstance(adapter, dict) else {} + target_capabilities = sorted_strings(target_contract.get("declared_capabilities")) + declared_capabilities.update(target_capabilities) + add_check( + checks, + failures, + f"permission-{target}-contract", + bool(target_contract), + f"{target} adapter exposes target permission contract for installer enforcement", + ) + if not target_capabilities: + continue + for capability in target_capabilities: + approval = capabilities.get(capability, {}) if isinstance(capabilities, dict) else {} + enforcement = approval.get("target_enforcement", {}) if isinstance(approval.get("target_enforcement"), dict) else {} + expires_at = str(approval.get("expires_at", "")).strip() + expiry_date = parse_date(expires_at) if expires_at else None + common_ok = ( + approval.get("decision") == "approved" + and bool(str(approval.get("reviewer", "")).strip()) + and bool(str(approval.get("scope", "")).strip()) + and bool(str(approval.get("reason", "")).strip()) + and isinstance(approval.get("evidence"), list) + and bool(approval.get("evidence")) + and bool(enforcement) + and expiry_date is not None + and (generated_date is None or expiry_date >= generated_date) + ) + add_check( + checks, + failures, + f"permission-{target}-{capability}-approved", + bool(common_ok), + f"{target} capability {capability} has active reviewer approval", + ) + target_enforcement_ok = bool(str(enforcement.get(target, "")).strip()) + add_check( + checks, + failures, + f"permission-{target}-{capability}-target-enforcement", + target_enforcement_ok, + f"{target} capability {capability} has target enforcement note", + ) + if common_ok and target_enforcement_ok: + enforced_count += 1 + + return { + "installer_permission_enforced_count": enforced_count, + "installer_permission_failure_count": len(failures) - permission_failure_start, + "permission_target_count": len(targets), + "permission_capability_count": len(declared_capabilities), + } + + +def simulate_install(skill_dir: Path, package_dir: Path, install_root: Path | None, generated_at: str) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + package_dir = package_dir.resolve() + checks: list[dict[str, str]] = [] + failures: list[str] = [] + warnings: list[str] = [] + package_manifest = load_json(package_dir / "manifest.json") + package_root = package_name(package_manifest, skill_dir) + archive_path = package_dir / f"{package_root}.zip" + installed_dir: Path | None = None + archive_entries: list[str] = [] + + temp_dir: tempfile.TemporaryDirectory[str] | None = None + if install_root is None: + temp_dir = tempfile.TemporaryDirectory(prefix="yao-skill-install-") + install_base = Path(temp_dir.name) + install_root_is_temp = True + else: + requested_root = install_root.resolve() + requested_root.mkdir(parents=True, exist_ok=True) + install_base = requested_root / f"simulate-{package_root}" + if install_base.exists(): + shutil.rmtree(install_base) + install_base.mkdir(parents=True, exist_ok=True) + install_root_is_temp = False + + try: + add_check(checks, failures, "archive-present", archive_path.exists(), f"Package archive exists: {display_path(archive_path)}") + if archive_path.exists(): + try: + archive_entries = zip_names(archive_path) + except zipfile.BadZipFile: + add_check(checks, failures, "archive-readable", False, f"Archive is not a readable zip: {display_path(archive_path)}") + else: + unsafe_entries = unsafe_zip_entries(archive_entries) + nested_skill_entries = non_root_skill_entries(archive_entries, package_root) + add_check(checks, failures, "archive-safe-paths", not unsafe_entries, "Archive has no absolute or parent-traversal entries") + add_check( + checks, + failures, + "single-skill-entrypoint", + not nested_skill_entries, + "Installed package exposes only the root SKILL.md entrypoint", + ) + roots = top_level_dirs(archive_entries) + add_check(checks, failures, "single-top-level", roots == [package_root], f"Archive top-level directory is {package_root}") + if not unsafe_entries and roots: + with zipfile.ZipFile(archive_path) as archive: + archive.extractall(install_base) + installed_dir = install_base / roots[0] + + frontmatter = read_frontmatter(installed_dir / "SKILL.md") if installed_dir else {} + source_manifest = load_json(installed_dir / "manifest.json") if installed_dir else {} + interface_doc = load_yaml(installed_dir / "agents" / "interface.yaml") if installed_dir else {} + add_check(checks, failures, "entrypoint-load", bool(frontmatter), "Installed SKILL.md frontmatter is readable") + add_check(checks, failures, "entrypoint-name", frontmatter.get("name") == package_root, "Installed SKILL.md name matches package directory") + add_check(checks, failures, "entrypoint-description", bool(frontmatter.get("description")), "Installed SKILL.md description is present") + add_check(checks, failures, "manifest-load", bool(source_manifest), "Installed manifest.json is readable") + add_check(checks, failures, "manifest-name", source_manifest.get("name") == package_manifest.get("name"), "Installed manifest name matches package manifest") + add_check(checks, failures, "manifest-version", source_manifest.get("version") == package_manifest.get("version"), "Installed manifest version matches package manifest") + add_check(checks, failures, "interface-load", bool(interface_doc.get("interface")), "Installed agents/interface.yaml is readable") + add_check( + checks, + failures, + "overview-report", + installed_dir is not None and (installed_dir / "reports" / "skill-overview.html").exists(), + "Installed overview report is present", + ) + add_check( + checks, + failures, + "review-studio-report", + installed_dir is not None and (installed_dir / "reports" / "review-studio.html").exists(), + "Installed Review Studio report is present", + ) + + adapter_root = package_dir + adapters = adapter_targets(adapter_root) + for target in adapters: + adapter = load_json(adapter_root / "targets" / target / "adapter.json") + add_check(checks, failures, f"adapter-{target}", bool(adapter), f"{target} adapter is readable after package install simulation") + add_check( + checks, + failures, + f"adapter-{target}-name", + adapter.get("name") == package_manifest.get("name"), + f"{target} adapter name matches package manifest", + ) + + permission_summary = permission_policy_checks(installed_dir, adapter_root, adapters, generated_at, checks, failures) + if not adapters: + warnings.append("No target adapters found in package directory.") + + if install_root_is_temp: + install_root_display = "[temporary-install-root]" + installed_dir_display = f"{install_root_display}/{installed_dir.name}" if installed_dir else "" + else: + install_root_display = display_path(install_base) + installed_dir_display = display_path(installed_dir) if installed_dir else "" + + report = { + "ok": not failures, + "schema_version": "2.0", + "generated_at": generated_at, + "skill_dir": display_path(skill_dir), + "package_dir": display_path(package_dir), + "install_root": install_root_display, + "installed_skill_dir": installed_dir_display, + "summary": { + "archive_present": archive_path.exists(), + "archive_entry_count": len(archive_entries), + "nested_skill_entry_count": len(non_root_skill_entries(archive_entries, package_root)), + "archive_extracted": bool(installed_dir and installed_dir.exists()), + "entrypoint_loaded": bool(frontmatter), + "manifest_loaded": bool(source_manifest), + "interface_loaded": bool(interface_doc.get("interface")), + "adapter_count": len(adapters), + **permission_summary, + "install_root_is_temp": install_root_is_temp, + "failure_count": len(failures), + "warning_count": len(warnings), + }, + "checks": checks, + "failures": failures, + "warnings": warnings, + "artifacts": { + "archive": display_path(archive_path) if archive_path.exists() else "", + "package_manifest": display_path(package_dir / "manifest.json"), + }, + } + return report + finally: + if temp_dir is not None: + temp_dir.cleanup() + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Install Simulation", + "", + f"- OK: `{report['ok']}`", + f"- Package directory: `{report['package_dir']}`", + f"- Archive extracted: `{summary['archive_extracted']}`", + f"- Nested SKILL.md entries: `{summary.get('nested_skill_entry_count', 0)}`", + f"- Entrypoint loaded: `{summary['entrypoint_loaded']}`", + f"- Manifest loaded: `{summary['manifest_loaded']}`", + f"- Interface loaded: `{summary['interface_loaded']}`", + f"- Adapters readable: `{summary['adapter_count']}`", + f"- Installer permissions enforced: `{summary.get('installer_permission_enforced_count', 0)}`", + f"- Installer permission failures: `{summary.get('installer_permission_failure_count', 0)}`", + f"- Failures: `{summary['failure_count']}`", + f"- Warnings: `{summary['warning_count']}`", + "", + "## Checks", + "", + "| Check | Status | Detail |", + "| --- | --- | --- |", + ] + for item in report["checks"]: + lines.append(f"| `{item['id']}` | `{item['status']}` | {item['detail']} |") + lines.extend(["", "## Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) + lines.extend(["", "## Warnings", ""]) + lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"]) + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Simulate installing a generated skill package into a temporary local skill root.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--package-dir", default="dist") + parser.add_argument("--install-root") + parser.add_argument("--output-json", default="reports/install_simulation.json") + parser.add_argument("--output-md", default="reports/install_simulation.md") + parser.add_argument("--generated-at", default=str(date.today())) + args = parser.parse_args() + + package_dir = Path(args.package_dir) + if not package_dir.is_absolute(): + package_dir = Path.cwd() / package_dir + install_root = Path(args.install_root) if args.install_root else None + report = simulate_install(Path(args.skill_dir), package_dir, install_root, args.generated_at) + + output_json = Path(args.output_json) + output_md = Path(args.output_md) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + report["artifacts"]["json"] = display_path(output_json) + report["artifacts"]["markdown"] = display_path(output_md) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/skill_ir_paths.py b/scripts/skill_ir_paths.py new file mode 100644 index 0000000..b007be0 --- /dev/null +++ b/scripts/skill_ir_paths.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Shared Skill IR artifact discovery helpers.""" + +import json +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by compiler, registry, conformance, and report scripts to locate canonical Skill IR artifacts." + + +def display_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def candidate_paths(skill_dir: Path, name: str) -> list[Path]: + candidates = [ + skill_dir / "reports" / "skill-ir.json", + skill_dir / "skill-ir" / "examples" / f"{name}.json", + skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json", + ] + examples_dir = skill_dir / "skill-ir" / "examples" + if examples_dir.exists(): + for path in sorted(examples_dir.glob("*.json")): + if path not in candidates: + candidates.append(path) + seen: set[Path] = set() + unique: list[Path] = [] + for path in candidates: + if path in seen: + continue + seen.add(path) + unique.append(path) + return unique + + +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 find_skill_ir_path(skill_dir: Path, name: str, *, require_schema: bool = False, fallback_source: str = "") -> str: + for path in candidate_paths(skill_dir, name): + payload = load_json(path) + if not payload: + continue + if require_schema and not payload.get("schema_version"): + continue + return display_path(path, skill_dir) + return fallback_source + + +def find_skill_ir( + skill_dir: Path, + name: str, + *, + require_schema: bool = False, + fallback_source: str = "", +) -> tuple[dict[str, Any], str]: + for path in candidate_paths(skill_dir, name): + payload = load_json(path) + if not payload: + continue + if require_schema and not payload.get("schema_version"): + continue + return payload, display_path(path, skill_dir) + return {}, fallback_source diff --git a/scripts/skill_os2_coverage_markdown.py b/scripts/skill_os2_coverage_markdown.py new file mode 100644 index 0000000..d8bfee1 --- /dev/null +++ b/scripts/skill_os2_coverage_markdown.py @@ -0,0 +1,101 @@ +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_skill_os2_coverage.py to keep coverage data assembly separate from Markdown rendering." + + +def render_table(items: list[dict[str, Any]]) -> list[str]: + lines = ["| Item | Status | Current | Command | Test |", "| --- | --- | --- | --- | --- |"] + for item in items: + lines.append( + "| " + + " | ".join( + [ + item["label"].replace("|", "\\|"), + f"`{item['status']}`", + item["current"].replace("|", "\\|"), + f"`{item['command']}`", + f"`{item['test']}`", + ] + ) + + " |" + ) + return lines + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Skill OS 2.0 Blueprint Coverage", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- local blueprint ready: `{str(summary['local_blueprint_ready']).lower()}`", + f"- public world-class ready: `{str(summary['public_world_class_ready']).lower()}`", + f"- pass: `{summary['pass_count']}` / `{summary['item_count']}`", + f"- missing: `{summary['missing_count']}`", + f"- warn: `{summary['warn_count']}`", + f"- reference extensions: `{summary['extension_track_count']}`", + f"- extension covered: `{summary['extension_covered_count']}`", + f"- extension partial: `{summary['extension_partial_count']}`", + f"- extension planned: `{summary['extension_planned_count']}`", + f"- adaptive extension ready: `{str(summary['adaptive_extension_ready']).lower()}`", + f"- world-class evidence pending: `{summary['world_class_evidence_pending_count']}`", + "", + "This report maps the Skill OS 2.0 upgrade blueprint to concrete local artifacts, commands, and tests. It does not count pending human review, provider runs, metadata fallbacks, or planned work as public world-class evidence.", + "", + "## Core Modules", + "", + *render_table(report["modules"]), + "", + "## Recommended PR Coverage", + "", + *render_table(report["recommended_prs"]), + "", + "## Reference Extension Tracks", + "", + "| Track | Status | Current | Target | Next action |", + "| --- | --- | --- | --- | --- |", + ] + for item in report["reference_extension_tracks"]: + lines.append( + "| " + + " | ".join( + [ + item["label"].replace("|", "\\|"), + f"`{item['status']}`", + item["current"].replace("|", "\\|"), + item["target"].replace("|", "\\|"), + item["next_action"].replace("|", "\\|"), + ] + ) + + " |" + ) + lines.extend( + [ + "", + "These extension tracks come from the user-supplied 2.0 reference plan. They are tracked separately from the formal Skill OS blueprint so the report can distinguish landed local architecture from planned explainer/adaptor evolution.", + "", + "## Next Highest-Leverage Moves", + "", + ] + ) + lines.extend(f"- {item}" for item in report["next_highest_leverage"]) + lines.extend(["", "## Evidence Detail", ""]) + for item in report["modules"] + report["recommended_prs"] + report["reference_extension_tracks"]: + existing = [entry["path"] for entry in item["evidence"] if entry["exists"]] + missing = [entry["path"] for entry in item["evidence"] if not entry["exists"]] + lines.append(f"### {item['label']}") + lines.append("") + lines.append(f"- objective: {item['objective']}") + lines.append(f"- status: `{item['status']}`") + lines.append(f"- existing evidence: {', '.join(f'`{path}`' for path in existing) if existing else '`none`'}") + if missing: + lines.append(f"- missing evidence: {', '.join(f'`{path}`' for path in missing)}") + lines.append(f"- next action: {item['next_action']}") + lines.append("") + return "\n".join(lines).rstrip() + "\n" diff --git a/scripts/skill_report_charts.py b/scripts/skill_report_charts.py new file mode 100644 index 0000000..3587796 --- /dev/null +++ b/scripts/skill_report_charts.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +import html +import math + +from skill_report_i18n import bi_span, en_for + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_skill_overview.py to render inline SVG report charts." + +BRAND = "#1B365D" +BORDER = "#e8e6dc" +SOFT = "#faf9f5" +TEXT = "#141413" +MUTED = "#504e49" + + +def esc(value) -> str: + return html.escape(str(value)) + + +def svg_text(zh: str, en: str | None = None, **attrs) -> str: + def attr_name(key: str) -> str: + if key.endswith("_"): + key = key[:-1] + return key.replace("_", "-") + + attr = " ".join(f'{attr_name(key)}="{esc(value)}"' for key, value in attrs.items() if value is not None) + if attr: + attr = " " + attr + return ( + f'{esc(zh)}' + f'{esc(en_for(en if en is not None else zh))}' + ) + + +def figure(name: str, svg: str, caption: str, caption_en: str | None = None) -> str: + return ( + f'
    ' + f"{svg}" + f"
    {bi_span(caption, caption_en)}
    " + "
    " + ) + + +def render_radar(scorecard: dict) -> str: + keys = ["completeness_score", "trigger_score", "evidence_score", "maintainability_score", "portability_score"] + labels = [scorecard[key]["label"] for key in keys if key in scorecard] + scores = [scorecard[key]["score"] for key in keys if key in scorecard] + center = 150 + radius = 92 + rings = [] + for pct in (0.25, 0.5, 0.75, 1.0): + points = [] + for i in range(len(scores)): + angle = -math.pi / 2 + 2 * math.pi * i / len(scores) + points.append(f"{center + radius * pct * math.cos(angle):.1f},{center + radius * pct * math.sin(angle):.1f}") + rings.append(f'') + data_points = [] + label_nodes = [] + for i, score in enumerate(scores): + angle = -math.pi / 2 + 2 * math.pi * i / len(scores) + data_radius = radius * score / 100 + data_points.append(f"{center + data_radius * math.cos(angle):.1f},{center + data_radius * math.sin(angle):.1f}") + lx = center + (radius + 32) * math.cos(angle) + ly = center + (radius + 32) * math.sin(angle) + label_nodes.append(svg_text(labels[i], x=f"{lx:.1f}", y=f"{ly:.1f}", text_anchor="middle", dominant_baseline="middle")) + svg = ( + '' + + svg_text("评分雷达", x="20", y="28", class_="chart-title") + + "".join(rings) + + f'' + + "".join(label_nodes) + + "" + ) + return figure("radar", svg, "评分雷达展示结构完整度、触发边界、证据、维护和迁移的相对强弱。") + + +def render_flow(summary: dict) -> str: + labels = summary.get("flow", ["输入材料", "Skill 包体", "可复用能力"]) + nodes = [] + for index, label in enumerate(labels[:3]): + x = 38 + index * 210 + nodes.append( + f'' + f'{svg_text(label, x=str(x + 75), y="99", text_anchor="middle")}' + ) + svg = ( + '' + + svg_text("交付流程", x="20", y="28", class_="chart-title") + + '' + + "".join(nodes) + + "" + ) + return figure("flow", svg, "交付流程把用户输入、生成的包体和可复用能力放在一条线上。") + + +def render_matrix(profile: dict) -> str: + matrix = profile.get("matrix", {}) + x = 70 + matrix.get("execution_certainty", 60) * 3.8 + y = 430 - matrix.get("knowledge_density", 60) * 3.2 + svg = ( + '' + + svg_text("能力矩阵", x="20", y="30", class_="chart-title") + + f'' + + f'' + + f'' + + svg_text("执行确定性", x="260", y="424", text_anchor="middle") + + svg_text("知识密度", x="22", y="230", transform="rotate(-90 22 230)", text_anchor="middle") + + f'' + + svg_text(str(profile.get("task_family", "Skill workflow")), x=f"{x + 18:.1f}", y=f"{y + 5:.1f}") + + "" + ) + return figure("matrix", svg, "能力矩阵说明这个 Skill 更偏知识密集还是执行确定。") + + +def render_layers(principle: dict) -> str: + layers = principle.get("layers", ["入口层", "参考层", "脚本层", "评估层", "报告层"]) + blocks = [] + for index, layer in enumerate(layers[:5]): + y = 55 + index * 48 + blocks.append( + f'' + + svg_text(layer, x="260", y=str(y + 22), text_anchor="middle") + ) + svg = ( + '' + + svg_text("分层结构", x="20", y="30", class_="chart-title") + + "".join(blocks) + + "" + ) + return figure("layers", svg, "分层结构展示入口、参考、脚本、评估和报告如何各司其职。") + + +def render_risk_heatmap(risk: dict) -> str: + risks = risk.get("risks", []) + cells = [] + for impact in range(1, 4): + for probability in range(1, 4): + count = sum(1 for item in risks if item.get("impact") == impact and item.get("probability") == probability) + color = ["#faf9f5", "#EEF2F7", "#D0DCE9", BRAND][min(3, count)] + x = 80 + (probability - 1) * 86 + y = 58 + (3 - impact) * 66 + cells.append( + f'' + f'{count}' + ) + svg = ( + '' + + svg_text("风险热力", x="20", y="30", class_="chart-title") + + "".join(cells) + + svg_text("发生概率", x="210", y="278", text_anchor="middle") + + svg_text("影响程度", x="24", y="160", transform="rotate(-90 24 160)", text_anchor="middle") + + "" + ) + return figure("risk_heatmap", svg, "风险热力图用影响程度和发生概率标出当前治理重点。") + + +def render_asset_donut(assets: dict) -> str: + distribution = assets.get("distribution", [])[:6] + total = sum(item.get("value", 1) for item in distribution) or 1 + colors = [BRAND, "#2D5A8A", "#D0DCE9", "#E4ECF5", "#e8e6dc", "#504e49"] + offset = 0 + circles = [] + labels = [] + for index, item in enumerate(distribution): + value = item.get("value", 1) + dash = value / total * 100 + circles.append( + f'' + ) + offset += dash + labels.append(svg_text(str(item.get("label", "asset")), x="235", y=str(78 + index * 22))) + svg = ( + '' + + svg_text("资产分布", x="20", y="30", class_="chart-title") + + "".join(circles) + + svg_text(f'{assets.get("file_count", 0)}项', f'{assets.get("file_count", 0)} items', x="130", y="136", text_anchor="middle") + + "".join(labels) + + "" + ) + return figure("asset_donut", svg, "资产分布图展示当前包体的文件和目录重心。") + + +def render_timeline(roadmap: dict) -> str: + items = roadmap.get("items", [])[:3] + blocks = [] + for index, item in enumerate(items): + x = 60 + index * 190 + title = str(item.get("title", "升级")) + if len(title) > 18: + title = title[:17] + "…" + title_en = en_for(title) + if len(title_en) > 24: + title_en = title_en[:23] + "..." + blocks.append( + f'' + + svg_text(f"下一步 {index + 1}", f"Next {index + 1}", x=str(x), y="126", text_anchor="middle") + + svg_text(title, title_en, x=str(x), y="150", text_anchor="middle") + ) + svg = ( + '' + + svg_text("迭代时间", x="20", y="30", class_="chart-title") + + f'' + + "".join(blocks) + + "" + ) + return figure("timeline", svg, "迭代时间线把下一步升级收束成少数可执行动作。") + + +def render_chart_set(model: dict) -> dict: + return { + "radar": render_radar(model.get("scorecard", {})), + "flow": render_flow(model.get("skill_summary", {})), + "matrix": render_matrix(model.get("capability_profile", {})), + "layers": render_layers(model.get("principle_model", {})), + "risk_heatmap": render_risk_heatmap(model.get("risk_governance", {})), + "asset_donut": render_asset_donut(model.get("package_assets", {})), + "timeline": render_timeline(model.get("iteration_roadmap", {})), + } diff --git a/scripts/skill_report_i18n.py b/scripts/skill_report_i18n.py new file mode 100644 index 0000000..430e4f1 --- /dev/null +++ b/scripts/skill_report_i18n.py @@ -0,0 +1,386 @@ +"""Bilingual text helpers for the static skill overview report.""" + +import html +import re + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_skill_overview.py to keep bilingual report copy and fallback rules out of HTML rendering." + + +TEXT_ZH = { + "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.": "从工作流、提示词、对话记录、文档或笔记中创建、重构、评估和打包 agent skill;适用于新建 Skill、沉淀重复流程、改进现有 Skill、补充 eval 或团队复用打包。", + "Understand the request.": "理解用户请求。", + "Execute the main task.": "执行核心任务。", + "Validate the result.": "校验交付结果。", + "Understand the request": "理解用户请求。", + "Execute the main task": "执行核心任务。", + "Validate the result": "校验交付结果。", + "Decide whether the request should become a skill and choose the lightest fit.": "判断请求是否应该沉淀为 Skill,并选择最轻量可靠的模式。", + "Capture job, output, exclusions, constraints, and standards.": "捕捉任务、输出、排除项、约束和质量标准。", + "Run reference scan: external benchmarks first, user references second, local fit third; surface only uncertainty or conflict.": "运行参考扫描:先看外部 benchmark,再看用户材料,最后校验本地适配;只暴露不确定性或冲突。", + "Write the `description` early and test route quality before expanding the package.": "尽早写出 `description`,先测试路由质量,再扩展包体。", + "Add output-risk, artifact-design, prompt-quality, and system-model reports only when they matter.": "只在确有价值时添加 output-risk、artifact-design、prompt-quality 和 system-model 报告。", + "Use $yao-meta-skill to turn my workflow or notes into a reusable skill with lean structure, clear triggering, and the right evals.": "当你需要把工作流或笔记沉淀成结构精简、触发清晰且带必要 eval 的可复用 Skill 时使用 $yao-meta-skill。", + "Turn rough requests into a compact reusable demo skill.": "把粗糙请求整理成紧凑、可复用的演示 Skill。", + "Tighten trigger and exclusions": "收紧触发与排除边界", + "Add the first execution asset": "补上第一个执行资产", + "Promote from scaffold to production-ready": "从脚手架推进到生产可用", + "Borrow one proven pattern on purpose": "有选择地借鉴一个成熟模式", + "Harden portability semantics": "加固跨环境语义", + "Create an iteration evidence loop": "建立迭代证据回路", + "补齐世界证据": "补齐世界证据", + "The package needs clearer near-neighbor exclusions before it grows.": "在继续扩展前,需要先把相邻但不应触发的场景说清楚。", + "The package is still mostly prose. Add one asset that removes repeated manual work.": "当前包体仍偏文本说明,应先增加一个能减少重复人工操作的资产。", + "The first version exists; the next gain usually comes from adding the smallest useful gates.": "第一版已经存在,下一步收益通常来自补上最小但有效的质量门禁。", + "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.": "已经有公开 benchmark 对象,下一步应主动选择一个模式借鉴,而不是松散吸收所有做法。", + "The skill already signals reuse across environments, so contract clarity matters early.": "这个 Skill 已经面向跨环境复用,因此早期就需要把契约语义说清楚。", + "The package should show what changed and why after the first draft.": "第一版之后,包体应该能说明改了什么以及为什么改。", + "Add 3 to 5 should-trigger and should-not-trigger examples.": "增加 3 到 5 个应触发和不应触发的例子。", + "Refine the frontmatter description to name the recurring job and non-goals.": "精炼 frontmatter description,明确重复任务和非目标。", + "Run a first trigger evaluation pass before expanding the package.": "扩展包体前先跑一轮触发评估。", + "Move stable procedural guidance into references if users will need it repeatedly.": "如果用户会反复使用某段流程说明,把它沉淀到 references。", + "Create one deterministic helper script if a repeated step can be executed instead of described.": "如果某个重复步骤可以执行而不是描述,就沉淀成一个确定性 helper script。", + "Keep the main SKILL.md compact and route-oriented.": "保持主 SKILL.md 简洁,并围绕路由与入口组织。", + "Decide whether this skill is personal, team-reused, or library-grade.": "判断这个 Skill 是个人使用、团队复用,还是库级基础能力。", + "Add only the gates that match that risk level.": "只添加与风险等级匹配的质量门禁。", + "Record lifecycle metadata and review cadence once reuse becomes real.": "一旦进入真实复用,就记录生命周期元数据和评审节奏。", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.": "先判断要借鉴的是方法、结构、执行方式还是可迁移性,并且第一轮只借鉴其中一个。", + "Record what you will not borrow so the package stays light.": "记录本轮不借鉴的内容,避免包体过重。", + "Confirm activation mode, execution context, and trust assumptions.": "确认激活模式、执行上下文和信任假设。", + "Add or review degradation strategy for non-native targets.": "补充或复核非原生目标端的降级策略。", + "Package the skill once to verify adapter expectations.": "至少打包一次 Skill,用来验证 adapter 预期。", + "Generate the HTML skill report and keep it aligned with the package.": "生成 HTML Skill 报告,并保持它与包体内容一致。", + "Record reference scan choices and non-goals.": "记录参考扫描的取舍和非目标。", + "Capture the next iteration choice explicitly before adding more files.": "在继续增加文件前,明确记录下一轮迭代选择。", + "Cleaner routing and fewer accidental activations.": "路由更清晰,误触发更少。", + "Stronger execution quality without bloating the entrypoint.": "在不膨胀入口文件的前提下提升执行质量。", + "A clearer path from exploratory package to maintained asset.": "更清晰地从探索性包体走向可维护资产。", + "A cleaner package shape with less accidental over-design.": "包体形态更清晰,也减少偶然过度设计。", + "Safer cross-environment reuse with less target drift.": "跨环境复用更安全,目标漂移更少。", + "A clearer path for the next author or reviewer.": "让下一位作者或评审者更容易接手。", + "提交有效 intake packet,并让 ledger 通过 artifact SHA-256 校验。": "提交有效 intake packet,并让 ledger 通过 artifact SHA-256 校验。", + "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", +} + +TEXT_EN = { + "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。": "Turn one-off experience into a reusable, evaluable, and portable skill package.", + "Skill 作者、复用团队和后续 reviewer。": "Skill authors, reuse teams, and later reviewers.", + "创建完成后建议先打开 reports/skill-overview.html,再继续扩展包体。": "After creation, open reports/skill-overview.html before expanding the package further.", + "触发面保持精简,并锚定在 frontmatter description。": "The trigger surface stays lean and anchored in the frontmatter description.", + "已生成 Skill IR,核心语义可先于平台打包被审查和迁移。": "Skill IR is generated so core semantics can be reviewed and migrated before platform packaging.", + "已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。": "Target compilation evidence is generated to review how IR maps to OpenAI, Claude, generic, and other target contracts.", + "已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。": "Output Eval Lab scorecard is generated to compare with-skill and baseline output quality.", + "已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。": "Output Execution Runs is generated to distinguish recorded fixtures, command runs, and model-run evidence.", + "已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。": "Output Review Adjudication is generated to record blind-review decisions, agreement rate, and pending cases.", + "已生成 Runtime Conformance Matrix,可审查目标端消费能力。": "Runtime Conformance Matrix is generated to review target-side consumption capability.", + "已生成 Security Trust Report,可审查脚本、依赖、secret 和包完整性风险。": "Security Trust Report is generated to review scripts, dependencies, secrets, and package-integrity risk.", + "已生成 Skill Atlas,可审查多 Skill 组合中的路由冲突、过期资产和 owner 缺口。": "Skill Atlas is generated to review route collisions, stale assets, and owner gaps across a skill library.", + "已生成 Registry Audit,可审查版本、owner、license、checksum 和目标兼容矩阵。": "Registry Audit is generated to review version, owner, license, checksum, and target compatibility metadata.", + "已生成 Install Simulation,可审查 zip 解压、入口加载、接口元数据和 adapter 可读性。": "Install Simulation is generated to review zip extraction, entrypoint loading, interface metadata, and adapter readability.", + "已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。": "Adoption Drift Report is generated to turn local usage feedback into next-iteration signals.", + "已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。": "Review Waivers ledger is generated to record reviewer approval, rationale, scope, and expiry for accepted warning risk.", + "已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。": "Review Annotations ledger is generated to attach reviewer notes to gates, files, and line numbers.", + "已生成 Review Studio 2.0,可在一页中查看 blocker、warning、证据路径和发布闸门。": "Review Studio 2.0 is generated to inspect blockers, warnings, evidence paths, and release gates on one page.", + "已打包 agents/interface.yaml,便于后续做跨平台适配。": "Portable interface metadata is packaged for later adapter-based export.", + "长指导被拆到 references 中,入口文件可以保持轻量。": "Extended guidance is separated into references so the entrypoint can stay compact.", + "确定性辅助逻辑放在 scripts 中,而不是藏在提示词里。": "Deterministic helper logic lives in scripts instead of hidden prompt text.", + "包内包含可随 Skill 迁移的质量门禁或触发检查。": "The package includes portable quality gates or trigger checks.", + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。": "Use this report to quickly understand the generated skill's role, principles, trigger boundary, and deliverables.", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。": "Clarify the recurring job, real input shape, and deliverable output before adding references, scripts, or evals.", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。": "If the request is still fuzzy, tighten the boundary through intent dialogue before expanding the package.", + "尚未生成盲评审定报告。": "The blind review adjudication report has not been generated yet.", + "尚未生成输出执行证据报告。": "The output execution evidence report has not been generated yet.", + "先记录 reviewer 对 A/B 的选择,再打开答案 key 计算一致率。": "Record the reviewer's A/B choice before opening the answer key and calculating agreement.", + "缺少真实 reviewer 决策时只显示待评审,不伪造人工结论。": "When real reviewer decisions are missing, show pending status instead of fabricating human conclusions.", + "recorded fixture 只能证明可复现样本,不等同于模型执行。": "A recorded fixture proves reproducible samples only; it is not model execution.", + "只有 provider runner 返回 model metadata 时才计入 model-executed。": "Only provider runners that return model metadata count as model-executed.", + "SKILL.md 已存在,是 Skill 的入口。": "SKILL.md exists and acts as the skill entrypoint.", + "README.md 已存在,便于人工阅读。": "README.md exists for human-readable usage.", + "agents/interface.yaml 已存在,便于跨平台适配。": "agents/interface.yaml exists for cross-platform adaptation.", + "manifest.json 已存在,生命周期信息可追踪。": "manifest.json exists so lifecycle metadata is traceable.", + "reports/ 已存在,生成证据可以随包体迁移。": "reports/ exists so generated evidence can travel with the package.", + "references/ 已存在,长指导可以从入口文件拆出。": "references/ exists so long guidance can stay out of the entrypoint.", + "scripts/ 已存在,确定性逻辑有位置承载。": "scripts/ exists to hold deterministic logic.", + "evals/ 已存在,触发或质量检查可以随包体迁移。": "evals/ exists so trigger or quality checks can travel with the package.", + "frontmatter description 已存在,具备基础路由面。": "The frontmatter description exists, giving the skill a basic routing surface.", + "description 有足够长度说明任务边界。": "The description is long enough to explain the task boundary.", + "description 已包含使用场景或排除边界信号。": "The description includes usage-scenario or exclusion-boundary signals.", + "description 证据不足,触发边界不稳定。": "Description evidence is insufficient, so the trigger boundary is unstable.", + "description 偏短,建议补充输入、输出或非目标。": "The description is short; add inputs, outputs, or non-goals.", + "description 缺少明确使用场景或排除边界。": "The description lacks clear usage scenarios or exclusion boundaries.", + "evals/ 已存在,可承载触发样例或质量检查。": "evals/ exists and can hold trigger examples or quality checks.", + "evals/ 证据不足,误触发检查仍偏弱。": "evals/ evidence is insufficient, so accidental-trigger checks remain weak.", + "intent-confidence 报告已生成,可辅助判断触发稳定性。": "The intent-confidence report exists and helps judge trigger stability.", + "intent-confidence 证据不足。": "intent-confidence evidence is insufficient.", + "入口文件保持克制,可维护性较好。": "The entrypoint stays restrained, which supports maintainability.", + "入口文件偏长,建议继续拆到 references/。": "The entrypoint is long; continue splitting durable guidance into references/.", + "references/ 已承载扩展指导。": "references/ carries extended guidance.", + "references/ 证据不足,长指导可能堆在入口。": "references/ evidence is insufficient; long guidance may still be crowded into the entrypoint.", + "scripts/ 已承载确定性逻辑。": "scripts/ carries deterministic logic.", + "scripts/ 证据不足,重复执行逻辑可能仍靠人工。": "scripts/ evidence is insufficient; repeated execution may still rely on manual work.", + "evals/ 已承载可迁移检查。": "evals/ carries portable checks.", + "agents/interface.yaml 已存在。": "agents/interface.yaml exists.", + "manifest.json 已存在。": "manifest.json exists.", + "目标平台或 adapter target 已声明。": "Target platforms or adapter targets are declared.", + "目标平台证据不足。": "Target-platform evidence is insufficient.", + "入口文件未发现明显私有绝对路径。": "No obvious private absolute paths were found in the entrypoint.", + "入口文件含私有绝对路径,迁移风险较高。": "The entrypoint contains private absolute paths, increasing portability risk.", + "分数越高代表上下文成本越低。": "A higher score means lower context cost.", + "上下文成本处于可控区间。": "Context cost is within a controlled range.", + "上下文成本偏高,建议压缩入口或拆分 references。": "Context cost is high; compress the entrypoint or split references further.", + "手动触发 + description 路由": "Manual activation plus description-based routing", + "跨平台": "Cross-platform", + "本地复用": "Local reuse", + "输入材料": "Input material", + "用户提供的工作流、提示词、文档、记录或散乱笔记": "User-provided workflows, prompts, documents, records, or rough notes.", + "期望沉淀的复用场景、排除项、约束和质量标准": "The reusable scenario, exclusions, constraints, and quality standards to capture.", + "Skill 包体": "Skill package", + "可路由的 SKILL.md": "A routeable SKILL.md.", + "agents/interface.yaml 元数据": "agents/interface.yaml metadata.", + "必要的 references、scripts、evals、reports 证据": "Necessary references, scripts, evals, and reports evidence.", + "可复用能力": "Reusable capability", + "入口层": "Entrypoint layer", + "参考层": "Reference layer", + "脚本层": "Script layer", + "评估层": "Evaluation layer", + "报告层": "Report layer", + "评分雷达": "Rating Radar", + "交付流程": "Delivery Flow", + "能力矩阵": "Capability Matrix", + "分层结构": "Layered Structure", + "风险热力": "Risk Heatmap", + "资产分布": "Asset Distribution", + "迭代时间": "Iteration Timeline", + "执行确定性": "Execution certainty", + "知识密度": "Knowledge density", + "发生概率": "Probability", + "影响程度": "Impact", + "评分雷达展示结构完整度、触发边界、证据、维护和迁移的相对强弱。": "The radar chart compares completeness, trigger clarity, evidence, maintainability, and portability.", + "交付流程把用户输入、生成的包体和可复用能力放在一条线上。": "The delivery flow places user input, generated package, and reusable capability on one path.", + "能力矩阵说明这个 Skill 更偏知识密集还是执行确定。": "The capability matrix shows whether the skill leans toward knowledge density or execution certainty.", + "分层结构展示入口、参考、脚本、评估和报告如何各司其职。": "The layered structure shows how entrypoint, references, scripts, evals, and reports each carry a distinct role.", + "风险热力图用影响程度和发生概率标出当前治理重点。": "The risk heatmap marks governance priorities by impact and probability.", + "资产分布图展示当前包体的文件和目录重心。": "The asset distribution chart shows where files and directories are concentrated.", + "迭代时间线把下一步升级收束成少数可执行动作。": "The iteration timeline narrows the next upgrade into a few executable moves.", + "只需要一次性回答、没有复用价值的临时请求。": "One-off requests that do not need reusable skill behavior.", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。": "Requests to perform an adjacent task directly rather than create or use this skill.", + "缺少必要事实且用户不允许澄清的场景。": "Cases that lack required facts and do not allow clarification.", + "相邻任务需要先确认是否应转为独立 Skill。": "Adjacent tasks should first be checked for whether they need a separate skill.", + "不替代人工事实核查,也不静默扩大职责。": "Does not replace human fact checking or silently expand responsibility.", + "先改触发边界,再扩展工作流。": "Tighten trigger boundaries before expanding the workflow.", + "只把重复且稳定的步骤沉淀为脚本。": "Turn only repeated and stable steps into scripts.", + "每次升级后重新生成报告并检查分数原因。": "Regenerate the report after each upgrade and inspect score reasons.", + "先补证据和边界,再增加包体复杂度。": "Improve evidence and boundaries before adding package complexity.", + "补齐世界证据": "Close world-class evidence", + "提交有效 intake packet,并让 ledger 通过 artifact SHA-256 校验。": "Submit valid intake packets and let the ledger verify artifact SHA-256 digests.", + "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。": "Only after the ledger accepts all external and human evidence should the public world-class claim move to review.", + "缺少真实 provider 模型运行和 token metadata。": "Missing a real provider model run and token metadata.", + "盲评 pair 仍待真实 reviewer 决策。": "Blind-review pairs still need real reviewer decisions.", + "原生 runtime enforcement 仍待目标客户端或外部安装器证明。": "Native runtime enforcement still needs target-client or external-installer proof.", + "真实外部客户端 metadata-only 事件仍未导入。": "Real external-client metadata-only events have not been imported yet.", +} + +MODE_ZH = { + "scaffold": "脚手架", + "production": "生产", + "library": "库级", + "governed": "治理", + "manual": "手动", + "inline": "内联", + "agent-skills": "Agent Skills", +} + +PACKAGE_LABEL_ZH = { + "SKILL.md": "Skill 入口文件", + "README.md": "人类可读使用说明", + "agents/interface.yaml": "跨平台接口元数据", + "manifest.json": "生命周期与打包元数据", + "references": "扩展指导与复用资料", + "scripts": "确定性脚本或本地工具", + "evals": "触发与质量检查", + "reports": "生成的证据与总结报告", +} + +KIND_ZH = {"file": "文件", "folder": "目录"} + +LABEL_EN = { + "强项": "Strength", + "缺口": "Gap", + "保留并复用": "Keep", + "纳入下一轮修复": "Fix next", + "误触发风险": "Trigger risk", + "输出漂移风险": "Output drift risk", + "证据不足风险": "Evidence gap risk", + "包体膨胀风险": "Package bloat risk", + "跨平台迁移风险": "Portability risk", +} + +METRIC_LABEL_EN = { + "完整度": "Completeness", + "触发清晰": "Trigger clarity", + "证据充分": "Evidence depth", + "可维护性": "Maintainability", + "可迁移性": "Portability", + "上下文成本": "Context cost", +} + +WORLD_CLASS_LABEL_EN = { + "提供商留出": "provider holdout", + "人工盲评": "human adjudication", + "原生权限": "native permission", + "原生遥测": "native telemetry", +} + + +def contains_cjk(text: str) -> bool: + return any("\u4e00" <= char <= "\u9fff" for char in str(text)) + + +def zh_for(text: str) -> str: + value = str(text).strip() + if not value: + return "" + if value in TEXT_ZH: + return TEXT_ZH[value] + if value in TEXT_EN or contains_cjk(value): + return value + if value.startswith("Use this skill when the request matches:"): + return "当用户请求与该 Skill 的触发描述匹配时使用。" + if value.startswith("用户说出类似需求时:"): + return "当用户提出与该 Skill 触发描述相近的请求时使用。" + if value.startswith("Use $") and " when you need to " in value: + skill, need = value.removeprefix("Use ").split(" when you need to ", 1) + return f"当你需要{zh_for(need).rstrip('。')}时使用 `{skill}`。" + if value.startswith("Read the strongest pattern from "): + repo = value.removeprefix("Read the strongest pattern from ").rstrip(".") + return f"阅读 `{repo}` 中最值得借鉴的模式。" + if value.startswith("Primary prompt task family:"): + return "主要提示任务类型已记录在 prompt quality profile 中。" + if value.startswith("Complexity:"): + return "复杂度判断已记录在 prompt quality profile 中。" + if value.startswith("Stability:"): + return "系统稳定性评分已记录在 system model 中。" + if value.startswith("Owned job:"): + return "负责的核心任务已在 system model 中说明。" + if value.startswith("Leverage:"): + return "关键杠杆点已在 system model 中说明。" + return "原始说明可切换到英文查看;默认中文报告保留结论与结构说明。" + + +def en_for(text: str) -> str: + value = str(text).strip() + if not value: + return "" + if value in TEXT_EN: + return TEXT_EN[value] + if value in METRIC_LABEL_EN: + return METRIC_LABEL_EN[value] + if value.startswith("创建完成后建议先打开 ") and value.endswith(",再继续扩展包体。"): + path = value.removeprefix("创建完成后建议先打开 ").removesuffix(",再继续扩展包体。") + return f"After creation, open {path} before expanding the package further." + if value.startswith("交付结果:"): + return "Deliverables: " + value.removeprefix("交付结果:") + if value.startswith("能力类型:"): + return "Capability type: " + value.removeprefix("能力类型:") + if value.startswith("成熟度:"): + return "Maturity: " + value.removeprefix("成熟度:") + if value.startswith("触发强度:"): + return "Trigger strength: " + en_for(value.removeprefix("触发强度:")) + if value.startswith("复用范围:"): + return "Reuse scope: " + en_for(value.removeprefix("复用范围:")) + if value.startswith("评审进度:"): + return "Review progress: " + value.removeprefix("评审进度:") + if value.startswith("待评审:"): + return "Pending review: " + value.removeprefix("待评审:") + if value.startswith("一致率:"): + tail = value.removeprefix("一致率:") + return "Agreement rate: " + ("not available yet" if tail == "暂无" else tail) + if value.startswith("非法决策:"): + return "Invalid decisions: " + value.removeprefix("非法决策:") + if value.startswith("变体运行:"): + return "Variant runs: " + value.removeprefix("变体运行:") + if value.startswith("模型执行:"): + return "Model executions: " + value.removeprefix("模型执行:") + if value.startswith("记录样本:"): + return "Recorded fixtures: " + value.removeprefix("记录样本:") + if value.startswith("Token 估算:"): + return "Token estimates: " + value.removeprefix("Token 估算:") + match = re.match(r"^世界级证据仍有\s+(\d+)\s+项待补;公开完成态 claim 必须继续保持阻塞。$", value) + if match: + return f"World-class evidence still has {match.group(1)} pending item(s); public completion claims must stay blocked." + match = re.match(r"^补齐(.+?)证据:(.+)$", value) + if match: + label = WORLD_CLASS_LABEL_EN.get(match.group(1), match.group(1)) + return f"Close {label} evidence: {en_for(match.group(2))}" + match = re.match(r"^继续补齐剩余\s+(\d+)\s+项外部/人工证据,并保持 claim guard 为 pending 状态。$", value) + if match: + return f"Close the remaining {match.group(1)} external or human evidence item(s) and keep the claim guard pending." + match = re.match( + r"^按 ledger 总量继续补齐\s+(\d+)\s+项待补证据(外部\s+(\d+)\s+项、人工\s+(\d+)\s+项;未展开\s+(\d+)\s+项),并保持 claim guard 为 pending 状态。$", + value, + ) + if match: + return ( + f"Close all {match.group(1)} pending evidence item(s) in the ledger " + f"(external {match.group(2)}, human {match.group(3)}; {match.group(4)} not shown here) " + "and keep the claim guard pending." + ) + match = re.match(r"^已生成\s+(\d+)\s+/\s+(\d+)\s+类报告证据。$", value) + if match: + return f"Generated {match.group(1)} / {match.group(2)} evidence report types." + match = re.match(r"^(.+?)\s+已存在。$", value) + if match: + return f"{match.group(1)} exists." + match = re.match(r"^(.+?)\s+未发现或为空,完整度扣分。$", value) + if match: + return f"{match.group(1)} was not found or is empty, reducing completeness." + match = re.match(r"^(.+?)\s+证据不足。$", value) + if match: + return f"{match.group(1)} evidence is insufficient." + match = re.match(r"^SKILL\.md 约\s+(.+?)\s+个词/字。$", value) + if match: + return f"SKILL.md is about {match.group(1)} words/characters." + match = re.match(r"^入口约\s+(.+?)\s+个词/字,references 约\s+(.+?)\s+个词/字。$", value) + if match: + return f"Entrypoint is about {match.group(1)} words/characters; references are about {match.group(2)}." + match = re.match(r"^结构化 Skill 目录,共\s+(.+?)\s+类关键资产。$", value) + if match: + return f"Structured skill directory with {match.group(1)} key asset groups." + if value.startswith("证据不足:缺少 "): + return "Evidence gap: missing " + value.removeprefix("证据不足:缺少 ").rstrip("。") + "." + for metric_label in METRIC_LABEL_EN: + prefix = metric_label + "需要补强:" + if value.startswith(prefix): + return f"{METRIC_LABEL_EN[metric_label]} needs improvement: {en_for(value.removeprefix(prefix))}" + if value.startswith("Use this skill when the request matches:"): + return "Use this skill when the request matches the frontmatter description." + if value.startswith("用户说出类似需求时:"): + return "Use this skill when the user asks for a matching scenario." + if value.startswith("当你需要") and "时使用" in value: + return "Use this skill when the request matches its stated scenario." + if contains_cjk(value): + return "Skill-specific source text is authored in Chinese; switch to Simplified Chinese for the exact wording." + return value + + +def bi_span(zh: str, en: str | None = None) -> str: + english = en_for(en) if en is not None else en_for(zh) + return ( + f'{html.escape(str(zh))}' + f'{html.escape(str(english))}' + ) + + +def bi_item(text: str) -> str: + return bi_span(zh_for(text), en_for(text)) + + +def mode_zh(value: str) -> str: + return MODE_ZH.get(str(value), str(value)) + + +def readable_description_zh(description: str) -> str: + if contains_cjk(description): + return description + return "该 Skill 的触发描述来自 SKILL.md frontmatter;默认中文报告先呈现能力边界,原始英文描述可切换到英文查看。" diff --git a/scripts/skill_report_layout.py b/scripts/skill_report_layout.py new file mode 100644 index 0000000..cfcf03c --- /dev/null +++ b/scripts/skill_report_layout.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Static layout contract for skill overview report HTML.""" + +import html +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_skill_overview.py to keep overview report layout and CSS out of data rendering." + + +ROOT = Path(__file__).resolve().parent.parent +ASSET_DIR = ROOT / "assets" + + +def read_layout_asset(filename: str) -> str: + return (ASSET_DIR / filename).read_text(encoding="utf-8").strip() + + +def bi_span(zh: str, en: str) -> str: + return ( + f'{html.escape(str(zh))}' + f'{html.escape(str(en))}' + ) + + +def render_report_nav(nav_items: list[dict[str, Any]]) -> str: + return "".join( + f"{bi_span(str(item['label']), str(item['label_en']))}" + for item in nav_items + ) + + +def render_language_switch() -> str: + return ( + '
    ' + '' + '' + "
    " + ) + + +def skill_overview_css() -> str: + return read_layout_asset("skill-overview.css") + + +def skill_overview_script() -> str: + return read_layout_asset("skill-overview.js") diff --git a/scripts/skill_report_metrics.py b/scripts/skill_report_metrics.py new file mode 100644 index 0000000..9ec3cb9 --- /dev/null +++ b/scripts/skill_report_metrics.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +from pathlib import Path + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by skill_report_model.py to calculate overview report metrics." + +REPORT_EVIDENCE = [ + "skill-ir.json", + "compiled_targets.json", + "intent-dialogue.json", + "intent-confidence.json", + "reference-synthesis.json", + "output_quality_scorecard.json", + "conformance_matrix.json", + "security_trust_report.json", + "skill_atlas.json", + "registry_audit.json", + "package_verification.json", + "install_simulation.json", + "upgrade_check.json", + "adoption_drift_report.json", + "review_waivers.json", + "artifact-design-profile.json", + "prompt-quality-profile.json", + "system-model.json", + "iteration-directions.json", + "output-risk-profile.json", +] + + +def clamp(value: int) -> int: + return max(0, min(100, int(value))) + + +def metric(label: str, score: int, reasons: list[str]) -> dict: + return { + "label": label, + "score": clamp(score), + "reasons": [reason for reason in reasons if reason], + } + + +def text_or_empty(path: Path) -> str: + if not path.exists() or path.is_dir(): + return "" + return path.read_text(encoding="utf-8", errors="replace") + + +def has_files(path: Path) -> bool: + return path.exists() and path.is_dir() and any(path.iterdir()) + + +def parse_description(skill_text: str) -> str: + lines = skill_text.splitlines() + if not lines or lines[0].strip() != "---": + return "" + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return "" + for line in lines[1:end_index]: + if line.strip().startswith("description:"): + return line.split(":", 1)[1].strip().strip('"') + return "" + + +def approximate_words(text: str) -> int: + cjk = sum(1 for char in text if "\u4e00" <= char <= "\u9fff") + latin = len([token for token in text.replace("\n", " ").split(" ") if token.strip()]) + return cjk + latin + + +def completeness_metric(skill_dir: Path) -> dict: + checks = [ + ("SKILL.md", 22, "SKILL.md 已存在,是 Skill 的入口。"), + ("README.md", 10, "README.md 已存在,便于人工阅读。"), + ("agents/interface.yaml", 14, "agents/interface.yaml 已存在,便于跨平台适配。"), + ("manifest.json", 14, "manifest.json 已存在,生命周期信息可追踪。"), + ("reports", 14, "reports/ 已存在,生成证据可以随包体迁移。"), + ("references", 10, "references/ 已存在,长指导可以从入口文件拆出。"), + ("scripts", 8, "scripts/ 已存在,确定性逻辑有位置承载。"), + ("evals", 8, "evals/ 已存在,触发或质量检查可以随包体迁移。"), + ] + score = 0 + reasons = [] + for rel_path, weight, reason in checks: + target = skill_dir / rel_path + exists = target.exists() if target.suffix else has_files(target) + if rel_path in {"reports", "references", "scripts", "evals"}: + exists = has_files(target) + if exists: + score += weight + reasons.append(reason) + else: + reasons.append(f"{rel_path} 未发现或为空,完整度扣分。") + return metric("完整度", score, reasons[:5]) + + +def trigger_metric(skill_dir: Path) -> dict: + skill_text = text_or_empty(skill_dir / "SKILL.md") + description = parse_description(skill_text) + score = 0 + reasons = [] + if description: + score += 35 + reasons.append("frontmatter description 已存在,具备基础路由面。") + else: + reasons.append("description 证据不足,触发边界不稳定。") + if len(description) >= 40: + score += 20 + reasons.append("description 有足够长度说明任务边界。") + else: + reasons.append("description 偏短,建议补充输入、输出或非目标。") + if any(word in description.lower() for word in ("use", "when", "用于", "不要", "not")): + score += 15 + reasons.append("description 已包含使用场景或排除边界信号。") + else: + reasons.append("description 缺少明确使用场景或排除边界。") + if has_files(skill_dir / "evals"): + score += 15 + reasons.append("evals/ 已存在,可承载触发样例或质量检查。") + else: + reasons.append("evals/ 证据不足,误触发检查仍偏弱。") + if (skill_dir / "reports" / "intent-confidence.json").exists(): + score += 15 + reasons.append("intent-confidence 报告已生成,可辅助判断触发稳定性。") + else: + reasons.append("intent-confidence 证据不足。") + return metric("触发清晰", score, reasons[:5]) + + +def evidence_metric(skill_dir: Path) -> dict: + reports_dir = skill_dir / "reports" + present = [] + for name in REPORT_EVIDENCE: + if name == "skill-ir.json": + if (reports_dir / name).exists() or any((skill_dir / "skill-ir" / "examples").glob("*.json")): + present.append(name) + continue + if (reports_dir / name).exists(): + present.append(name) + score = round(len(present) / len(REPORT_EVIDENCE) * 100) + reasons = [] + if present: + reasons.append(f"已生成 {len(present)} / {len(REPORT_EVIDENCE)} 类报告证据。") + reasons.extend([f"{name} 已存在。" for name in present[:3]]) + missing = [name for name in REPORT_EVIDENCE if name not in present] + if missing: + reasons.append(f"证据不足:缺少 {', '.join(missing[:3])}。") + return metric("证据充分", score, reasons[:5]) + + +def maintainability_metric(skill_dir: Path) -> dict: + skill_text = text_or_empty(skill_dir / "SKILL.md") + words = approximate_words(skill_text) + score = 35 + reasons = [f"SKILL.md 约 {words} 个词/字。"] + if words <= 900: + score += 20 + reasons.append("入口文件保持克制,可维护性较好。") + else: + reasons.append("入口文件偏长,建议继续拆到 references/。") + if has_files(skill_dir / "references"): + score += 15 + reasons.append("references/ 已承载扩展指导。") + else: + reasons.append("references/ 证据不足,长指导可能堆在入口。") + if has_files(skill_dir / "scripts"): + score += 15 + reasons.append("scripts/ 已承载确定性逻辑。") + else: + reasons.append("scripts/ 证据不足,重复执行逻辑可能仍靠人工。") + if has_files(skill_dir / "evals"): + score += 15 + reasons.append("evals/ 已承载可迁移检查。") + else: + reasons.append("evals/ 证据不足。") + return metric("可维护性", score, reasons[:5]) + + +def portability_metric(skill_dir: Path) -> dict: + score = 0 + reasons = [] + if (skill_dir / "agents" / "interface.yaml").exists(): + score += 35 + reasons.append("agents/interface.yaml 已存在。") + else: + reasons.append("agents/interface.yaml 证据不足,跨平台接口不清晰。") + if (skill_dir / "manifest.json").exists(): + score += 25 + reasons.append("manifest.json 已存在。") + else: + reasons.append("manifest.json 证据不足,生命周期信息不完整。") + manifest_text = text_or_empty(skill_dir / "manifest.json") + interface_text = text_or_empty(skill_dir / "agents" / "interface.yaml") + if any(target in manifest_text + interface_text for target in ("openai", "claude", "generic")): + score += 25 + reasons.append("目标平台或 adapter target 已声明。") + else: + reasons.append("目标平台证据不足。") + skill_text = text_or_empty(skill_dir / "SKILL.md") + if "/Users/" not in skill_text and "C:\\" not in skill_text: + score += 15 + reasons.append("入口文件未发现明显私有绝对路径。") + else: + reasons.append("入口文件含私有绝对路径,迁移风险较高。") + return metric("可迁移性", score, reasons[:5]) + + +def context_cost_metric(skill_dir: Path) -> dict: + skill_words = approximate_words(text_or_empty(skill_dir / "SKILL.md")) + reference_words = 0 + references = skill_dir / "references" + if references.exists(): + for path in references.rglob("*"): + if path.is_file(): + reference_words += approximate_words(text_or_empty(path)) + total = skill_words + reference_words + if total <= 900: + score = 95 + elif total <= 1800: + score = 78 + elif total <= 3200: + score = 60 + else: + score = 42 + reasons = [ + f"入口约 {skill_words} 个词/字,references 约 {reference_words} 个词/字。", + "分数越高代表上下文成本越低。", + ] + if total > 1800: + reasons.append("上下文成本偏高,建议压缩入口或拆分 references。") + else: + reasons.append("上下文成本处于可控区间。") + return metric("上下文成本", score, reasons) + + +def calculate_scorecard(skill_dir: Path) -> dict: + skill_dir = skill_dir.resolve() + return { + "completeness_score": completeness_metric(skill_dir), + "trigger_score": trigger_metric(skill_dir), + "evidence_score": evidence_metric(skill_dir), + "maintainability_score": maintainability_metric(skill_dir), + "portability_score": portability_metric(skill_dir), + "context_cost": context_cost_metric(skill_dir), + } diff --git a/scripts/skill_report_model.py b/scripts/skill_report_model.py new file mode 100644 index 0000000..a269f3d --- /dev/null +++ b/scripts/skill_report_model.py @@ -0,0 +1,554 @@ +#!/usr/bin/env python3 +from datetime import date +from pathlib import Path + +from skill_ir_paths import find_skill_ir +from skill_report_metrics import calculate_scorecard +from skill_report_sources import ( + extract_title, + load_json, + load_yaml, + package_entries, + parse_frontmatter, + parse_sections, + summarize_logic, + summarize_usage, +) +from skill_report_sections import package_assets, quality_review, risk_governance +from skill_report_world_class import world_class_readiness, world_class_roadmap_item + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_skill_overview.py to build the v2 report data model." + +REPORT_NAV_V2 = [ + {"label": "技能概述", "label_en": "Overview", "href": "overview"}, + {"label": "总览指标", "label_en": "Metrics", "href": "metrics"}, + {"label": "能力画像", "label_en": "Profile", "href": "capability"}, + {"label": "原理结构", "label_en": "Principle", "href": "principle"}, + {"label": "契约边界", "label_en": "Contract", "href": "contract"}, + {"label": "质量评估", "label_en": "Quality", "href": "quality"}, + {"label": "风险治理", "label_en": "Risk", "href": "risk"}, + {"label": "包体资产", "label_en": "Assets", "href": "assets"}, + {"label": "迭代路线", "label_en": "Roadmap", "href": "roadmap"}, +] + + +def context_payload(intent: dict) -> dict: + context = intent.get("context", {}) if isinstance(intent, dict) else {} + return context if isinstance(context, dict) else {} + + +def as_text_list(value) -> list[str]: + if not value: + return [] + if isinstance(value, list): + return [str(item).strip() for item in value if str(item).strip()] + return [str(value).strip()] + + +def compact_unique(items: list[str], limit: int = 6) -> list[str]: + seen = set() + result = [] + for item in items: + normalized = str(item).strip() + if not normalized or normalized.lower() in seen: + continue + seen.add(normalized.lower()) + result.append(normalized) + if len(result) >= limit: + break + return result + + +def derive_strengths(skill_dir: Path, metadata: dict) -> list[str]: + strengths = ["触发面保持精简,并锚定在 frontmatter description。"] + if (skill_dir / "reports" / "skill-ir.json").exists() or (skill_dir / "skill-ir" / "examples").exists(): + strengths.append("已生成 Skill IR,核心语义可先于平台打包被审查和迁移。") + if (skill_dir / "reports" / "compiled_targets.json").exists(): + strengths.append("已生成目标编译报告,可审查 IR 到 OpenAI、Claude、generic 等目标契约的映射。") + if (skill_dir / "reports" / "output_quality_scorecard.json").exists(): + strengths.append("已生成 Output Eval Lab scorecard,可比较 with-skill 与 baseline 输出质量。") + if (skill_dir / "reports" / "output_execution_runs.json").exists(): + strengths.append("已生成 Output Execution Runs,可区分记录样本、命令执行和模型执行证据。") + if (skill_dir / "reports" / "output_review_adjudication.json").exists(): + strengths.append("已生成 Output Review Adjudication,可记录盲评决策、一致率和待评审项。") + if (skill_dir / "reports" / "conformance_matrix.json").exists(): + strengths.append("已生成 Runtime Conformance Matrix,可审查目标端消费能力。") + if (skill_dir / "reports" / "security_trust_report.json").exists(): + strengths.append("已生成 Security Trust Report,可审查脚本、依赖、secret 和包完整性风险。") + if (skill_dir / "reports" / "skill_atlas.json").exists(): + strengths.append("已生成 Skill Atlas,可审查多 Skill 组合中的路由冲突、过期资产和 owner 缺口。") + if (skill_dir / "reports" / "registry_audit.json").exists(): + strengths.append("已生成 Registry Audit,可审查版本、owner、license、checksum 和目标兼容矩阵。") + if (skill_dir / "reports" / "install_simulation.json").exists(): + strengths.append("已生成 Install Simulation,可审查 zip 解压、入口加载、接口元数据和 adapter 可读性。") + if (skill_dir / "reports" / "adoption_drift_report.json").exists(): + strengths.append("已生成 Adoption Drift Report,可把本地使用反馈转为下一轮迭代信号。") + if (skill_dir / "reports" / "review_waivers.json").exists(): + strengths.append("已生成 Review Waivers 台账,可记录 reviewer 对 warning 风险的批准、理由和到期时间。") + if (skill_dir / "reports" / "review_annotations.json").exists(): + strengths.append("已生成 Review Annotations 台账,可把 reviewer 批注挂到 gate、文件和行号。") + if (skill_dir / "reports" / "review-studio.json").exists(): + strengths.append("已生成 Review Studio 2.0,可在一页中查看 blocker、warning、证据路径和发布闸门。") + if (skill_dir / "agents" / "interface.yaml").exists(): + strengths.append("已打包 agents/interface.yaml,便于后续做跨平台适配。") + if (skill_dir / "references").exists() and any((skill_dir / "references").iterdir()): + strengths.append("长指导被拆到 references 中,入口文件可以保持轻量。") + if (skill_dir / "scripts").exists() and any((skill_dir / "scripts").iterdir()): + strengths.append("确定性辅助逻辑放在 scripts 中,而不是藏在提示词里。") + if (skill_dir / "evals").exists() and any((skill_dir / "evals").iterdir()): + strengths.append("包内包含可随 Skill 迁移的质量门禁或触发检查。") + if metadata.get("maturity_tier"): + strengths.append(f"生命周期元数据清晰,成熟度层级为 `{metadata['maturity_tier']}`。") + return strengths[:6] + + +def scenario_items(description: str, usage_steps: list[str], metadata: dict) -> list[str]: + scenarios = [] + if "workflow" in description.lower() or "流程" in description: + scenarios.append("把重复流程沉淀为可复用的 agent skill。") + if "prompt" in description.lower() or "提示" in description: + scenarios.append("把分散提示词、对话记录或操作规范整理为稳定能力。") + if metadata.get("maturity_tier") in {"production", "library", "governed"}: + scenarios.append("团队复用前,需要明确触发边界、质量证据和维护责任。") + if usage_steps: + scenarios.append(f"用户说出类似需求时:{usage_steps[0]}") + scenarios.append("已有原始资料,但还没有清晰输入、输出和后续迭代路径。") + return compact_unique(scenarios, limit=4) + + +def trigger_contract(interface_data: dict, description: str) -> dict: + compatibility = interface_data.get("compatibility", {}) + activation = compatibility.get("activation", {}) + execution = compatibility.get("execution", {}) + return { + "description": description, + "activation": activation.get("mode", "manual"), + "execution": execution.get("context", "inline"), + "shell": execution.get("shell", "bash"), + } + + +def io_contract(intent: dict, package_map: list[dict], description: str) -> dict: + context = context_payload(intent) + inputs = as_text_list(context.get("real_inputs")) or [ + "用户提供的工作流、提示词、文档、记录或散乱笔记", + "期望沉淀的复用场景、排除项、约束和质量标准", + ] + outputs = as_text_list(context.get("primary_output")) or [ + "可路由的 SKILL.md", + "agents/interface.yaml 元数据", + "必要的 references、scripts、evals、reports 证据", + ] + if package_map: + outputs.append(f"结构化 Skill 目录,共 {len(package_map)} 类关键资产。") + if description and not outputs: + outputs.append(f"围绕该能力边界交付:{description}") + return { + "inputs": compact_unique(inputs, limit=5), + "outputs": compact_unique(outputs, limit=5), + } + + +def principle_nodes(system_model: dict) -> list[dict]: + boundary = system_model.get("boundary_map", {}) if isinstance(system_model, dict) else {} + loops = system_model.get("feedback_loops", []) if isinstance(system_model, dict) else [] + drift = system_model.get("drift_watch", []) if isinstance(system_model, dict) else [] + leverage = system_model.get("leverage_points", []) if isinstance(system_model, dict) else [] + return [ + {"title": "意图澄清", "body": boundary.get("owned_job", "先确认真实任务、输入材料和交付结果。")}, + {"title": "边界路由", "body": "用 frontmatter description 决定是否触发,并写明相邻非目标。"}, + {"title": "资产分层", "body": "把入口、参考、脚本、评估和报告拆到各自目录,避免 SKILL.md 膨胀。"}, + { + "title": "证据回路", + "body": loops[0].get("response") if loops else "通过评估、报告和复盘把真实使用反馈变成下一轮改进。", + }, + { + "title": "漂移观察", + "body": drift[0].get("countermeasure") if drift else "持续观察触发漂移、输出漂移和治理漂移。", + }, + { + "title": "杠杆升级", + "body": leverage[0].get("move") if leverage else "优先改边界、触发和一个最小自修复检查。", + }, + ] + + +def roadmap_items(iteration: dict, readiness: dict | None = None) -> list[dict]: + directions = iteration.get("directions", []) if isinstance(iteration, dict) else [] + items = [] + evidence_item = world_class_roadmap_item(readiness or {}) + if evidence_item: + items.append(evidence_item) + for item in directions[:3]: + items.append( + { + "title": item.get("title", "下一步"), + "why": item.get("why", "提升复用稳定性。"), + "actions": item.get("actions", [])[:3], + "unlocks": item.get("unlocks", ""), + } + ) + if len(items) >= 3: + break + if items: + return items + return [ + { + "title": "收紧触发", + "why": "先让 Skill 在正确场景被调用,再扩展资产。", + "actions": ["增加正例、反例和近邻用例。", "压缩 frontmatter description。"], + "unlocks": "更稳定的路由边界。", + } + ] + + +def capability_profile(manifest: dict, interface_data: dict, prompt_quality: dict) -> dict: + maturity = manifest.get("maturity_tier", "scaffold") + task_family = prompt_quality.get("primary_task_family", {}).get("label", "Skill workflow") + execution = interface_data.get("compatibility", {}).get("execution", {}) + adapter_targets = interface_data.get("compatibility", {}).get("adapter_targets", []) + certainty = 72 if execution.get("context", "inline") == "inline" else 58 + knowledge = 80 if prompt_quality.get("complexity", {}).get("band") in {"expert", "complex"} else 62 + return { + "archetype": manifest.get("skill_archetype", maturity), + "task_family": task_family, + "maturity": maturity, + "trigger_strength": "手动触发 + description 路由", + "reuse_scope": "跨平台" if len(adapter_targets) >= 2 else "本地复用", + "matrix": {"execution_certainty": certainty, "knowledge_density": knowledge}, + } + + +def build_report_model(skill_dir: Path) -> dict: + skill_dir = skill_dir.resolve() + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, body = parse_frontmatter(skill_text) + sections = parse_sections(body) + interface_data = load_yaml(skill_dir / "agents" / "interface.yaml") + manifest = load_json(skill_dir / "manifest.json") + intent = load_json(skill_dir / "reports" / "intent-confidence.json") + artifact_design = load_json(skill_dir / "reports" / "artifact-design-profile.json") + prompt_quality = load_json(skill_dir / "reports" / "prompt-quality-profile.json") + system_model = load_json(skill_dir / "reports" / "system-model.json") + output_risk = load_json(skill_dir / "reports" / "output-risk-profile.json") + output_quality = load_json(skill_dir / "reports" / "output_quality_scorecard.json") + output_execution = load_json(skill_dir / "reports" / "output_execution_runs.json") + output_blind_review = load_json(skill_dir / "reports" / "output_blind_review_pack.json") + output_review_kit = load_json(skill_dir / "reports" / "output_review_kit.json") + output_review_adjudication = load_json(skill_dir / "reports" / "output_review_adjudication.json") + benchmark_reproducibility = load_json(skill_dir / "reports" / "benchmark_reproducibility.json") + conformance = load_json(skill_dir / "reports" / "conformance_matrix.json") + runtime_permissions = load_json(skill_dir / "reports" / "runtime_permission_probes.json") + trust_report = load_json(skill_dir / "reports" / "security_trust_report.json") + skill_atlas = load_json(skill_dir / "reports" / "skill_atlas.json") + registry_audit = load_json(skill_dir / "reports" / "registry_audit.json") + package_verification = load_json(skill_dir / "reports" / "package_verification.json") + install_simulation = load_json(skill_dir / "reports" / "install_simulation.json") + upgrade_check = load_json(skill_dir / "reports" / "upgrade_check.json") + adoption_drift = load_json(skill_dir / "reports" / "adoption_drift_report.json") + review_waivers = load_json(skill_dir / "reports" / "review_waivers.json") + review_annotations = load_json(skill_dir / "reports" / "review_annotations.json") + world_class_evidence = load_json(skill_dir / "reports" / "world_class_evidence_plan.json") + world_class_evidence_ledger = load_json(skill_dir / "reports" / "world_class_evidence_ledger.json") + compiled_targets = load_json(skill_dir / "reports" / "compiled_targets.json") + reference_synthesis = load_json(skill_dir / "reports" / "reference-synthesis.json") + iteration = load_json(skill_dir / "reports" / "iteration-directions.json") + + name = frontmatter.get("name", skill_dir.name) + skill_ir, skill_ir_path = find_skill_ir(skill_dir, name) + description = frontmatter.get("description", "No description found.") + title = extract_title(body, name.replace("-", " ").title()) + display_name = interface_data.get("interface", {}).get("display_name", title) + default_prompt = interface_data.get("interface", {}).get("default_prompt", "") + logic_steps = summarize_logic(sections) + usage_steps = summarize_usage(sections, default_prompt, description) + package_map = package_entries(skill_dir) + scorecard = calculate_scorecard(skill_dir) + strengths = derive_strengths(skill_dir, manifest) + trigger = trigger_contract(interface_data, description) + io = io_contract(intent, package_map, description) + principles = principle_nodes(system_model) + readiness = world_class_readiness(world_class_evidence_ledger) + roadmap = roadmap_items(iteration, readiness) + metadata = { + "canonical_format": interface_data.get("compatibility", {}).get("canonical_format", "agent-skills"), + "targets": interface_data.get("compatibility", {}).get("adapter_targets", []), + "maturity_tier": manifest.get("maturity_tier", "scaffold"), + "skill_archetype": manifest.get("skill_archetype", manifest.get("maturity_tier", "scaffold")), + "updated_at": manifest.get("updated_at", str(date.today())), + } + deliverables = [ + "SKILL.md", + "agents/interface.yaml", + skill_ir_path or "reports/skill-ir.json", + "reports/compiled_targets.md", + "reports/output_quality_scorecard.md", + "reports/conformance_matrix.md", + "reports/security_trust_report.md", + "reports/skill_atlas.html", + "reports/registry_audit.md", + "reports/package_verification.md", + "reports/install_simulation.md", + "reports/upgrade_check.md", + "reports/adoption_drift_report.md", + "reports/review_waivers.md", + "reports/review_annotations.md", + "reports/review-studio.html", + "reports/skill-interpretation.html", + "reports/skill-overview.html", + ] + if (skill_dir / "reports" / "runtime_permission_probes.md").exists(): + insert_after = deliverables.index("reports/security_trust_report.md") + 1 + deliverables.insert(insert_after, "reports/runtime_permission_probes.md") + if (skill_dir / "reports" / "output_blind_review_pack.md").exists(): + insert_after = deliverables.index("reports/output_quality_scorecard.md") + 1 + deliverables.insert(insert_after, "reports/output_blind_review_pack.md") + if (skill_dir / "reports" / "output_execution_runs.md").exists(): + insert_after = deliverables.index("reports/output_quality_scorecard.md") + 1 + deliverables.insert(insert_after, "reports/output_execution_runs.md") + if (skill_dir / "reports" / "output_blind_answer_key.json").exists(): + insert_after = deliverables.index("reports/output_blind_review_pack.md") + 1 if "reports/output_blind_review_pack.md" in deliverables else deliverables.index("reports/output_quality_scorecard.md") + 1 + deliverables.insert(insert_after, "reports/output_blind_answer_key.json") + if (skill_dir / "reports" / "output_review_kit.md").exists(): + insert_after = deliverables.index("reports/output_blind_review_pack.md") + 1 if "reports/output_blind_review_pack.md" in deliverables else deliverables.index("reports/output_quality_scorecard.md") + 1 + deliverables.insert(insert_after, "reports/output_review_kit.md") + if (skill_dir / "reports" / "output_review_adjudication.md").exists(): + insert_after = deliverables.index("reports/output_review_kit.md") + 1 if "reports/output_review_kit.md" in deliverables else deliverables.index("reports/output_quality_scorecard.md") + 1 + deliverables.insert(insert_after, "reports/output_review_adjudication.md") + if (skill_dir / "reports" / "benchmark_reproducibility.md").exists(): + insert_after = deliverables.index("reports/output_review_adjudication.md") + 1 if "reports/output_review_adjudication.md" in deliverables else deliverables.index("reports/output_quality_scorecard.md") + 1 + deliverables.insert(insert_after, "reports/benchmark_reproducibility.md") + if (skill_dir / "reports" / "world_class_evidence_plan.md").exists(): + insert_after = deliverables.index("reports/review_waivers.md") + 1 + deliverables.insert(insert_after, "reports/world_class_evidence_plan.md") + if (skill_dir / "reports" / "world_class_evidence_ledger.md").exists(): + insert_after = ( + deliverables.index("reports/world_class_evidence_plan.md") + 1 + if "reports/world_class_evidence_plan.md" in deliverables + else deliverables.index("reports/review_waivers.md") + 1 + ) + deliverables.insert(insert_after, "reports/world_class_evidence_ledger.md") + + skill_summary = { + "name": name, + "title": title, + "display_name": display_name, + "description": description, + "maturity": metadata["maturity_tier"], + "updated_at": metadata["updated_at"], + "core_value": "把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。", + "audience": "Skill 作者、复用团队和后续 reviewer。", + "deliverables": deliverables, + "flow": ["输入材料", "Skill 包体", "可复用能力"], + } + contract = { + "trigger": trigger, + "inputs": io["inputs"], + "outputs": io["outputs"], + "should_trigger": scenario_items(description, usage_steps, manifest)[:3], + "should_not_trigger": [ + "只需要一次性回答、没有复用价值的临时请求。", + "要求直接执行相邻任务,而不是沉淀或使用这个 Skill。", + "缺少必要事实且用户不允许澄清的场景。", + ], + "boundary_cards": [ + {"label": "Owned", "body": description}, + {"label": "Adjacent", "body": "相邻任务需要先确认是否应转为独立 Skill。"}, + {"label": "Excluded", "body": "不替代人工事实核查,也不静默扩大职责。"}, + ], + } + synthesis = reference_synthesis.get("synthesis", {}).get("borrow_now", [])[:3] + q_review = quality_review(strengths, scorecard, artifact_design, prompt_quality, system_model) + report_contract = { + "schema_version": "2.0", + "html_report": "reports/skill-overview.html", + "language": "zh-CN", + "default_language": "zh-CN", + "languages": ["zh-CN", "en"], + "layout": "kami-white-audit-v2", + "nav_labels": [item["label"] for item in REPORT_NAV_V2], + "nav_labels_en": [item["label_en"] for item in REPORT_NAV_V2], + } + model = { + "skill_summary": skill_summary, + "scorecard": scorecard, + "capability_profile": capability_profile(manifest, interface_data, prompt_quality), + "principle_model": {"nodes": principles, "layers": ["入口层", "参考层", "脚本层", "评估层", "报告层"]}, + "contract_boundary": contract, + "quality_review": q_review, + "risk_governance": risk_governance(output_risk, system_model, scorecard), + "world_class_readiness": readiness, + "package_assets": package_assets(package_map), + "iteration_roadmap": {"items": roadmap}, + "report_contract": report_contract, + # Backward-compatible fields consumed by existing review tooling. + "name": name, + "title": title, + "display_name": display_name, + "description": description, + "logic_steps": logic_steps, + "usage_steps": usage_steps, + "package_map": package_map, + "strengths": strengths, + "scenarios": scenario_items(description, usage_steps, manifest), + "trigger_contract": trigger, + "io_contract": io, + "principles": principles, + "roadmap": roadmap, + "cards": [], + "introduction": [ + "这份报告用于快速理解新生成 Skill 的定位、原理、触发边界和交付内容。", + "先确认重复任务、真实输入形态和可交付输出,再决定是否继续加 references、scripts 或 evals。", + "如果需求仍然模糊,优先回到 intent dialogue 收紧边界,再扩展包体结构。", + ], + "benchmark_highlights": [], + "skill_ir": { + "schema_version": skill_ir.get("schema_version", ""), + "source_path": skill_ir_path, + "target_count": len(skill_ir.get("targets", [])), + "trigger_samples": len(skill_ir.get("trigger_surface", {}).get("should_trigger", [])), + "output_eval_cases": len(skill_ir.get("eval_plan", {}).get("output", [])), + }, + "compiled_targets": { + "ok": compiled_targets.get("ok", False), + "schema_version": compiled_targets.get("schema_version", ""), + "summary": compiled_targets.get("summary", {}), + "targets": [ + { + "target": item.get("target", ""), + "status": item.get("status", ""), + "adapter_mode": item.get("target_transform", {}).get("adapter_mode", ""), + "degradation_strategy": item.get("target_transform", {}).get("degradation_strategy", ""), + "native_surface": item.get("target_native_contract", {}).get("native_surface", ""), + "permission_enforcement": item.get("target_native_contract", {}).get("permissions", {}).get("enforcement", ""), + "generated_files": item.get("target_transform", {}).get("generated_files", []), + "unsupported_features": item.get("unsupported_features", []), + "warnings": item.get("warnings", []), + } + for item in compiled_targets.get("targets", []) + if isinstance(item, dict) + ], + "failures": compiled_targets.get("failures", []), + "warnings": compiled_targets.get("warnings", []), + }, + "output_quality": output_quality.get("summary", {}), + "output_execution": { + "ok": output_execution.get("ok", False), + "summary": output_execution.get("summary", {}), + "runner": output_execution.get("runner", {}), + "failures": output_execution.get("failures", []), + }, + "output_blind_review": { + "summary": output_blind_review.get("summary", {}), + "seed": output_blind_review.get("seed", ""), + "pair_count": output_blind_review.get("summary", {}).get("pair_count", 0), + "answer_key_separate": output_blind_review.get("summary", {}).get("answer_key_separate", False), + }, + "output_review_kit": { + "ok": output_review_kit.get("ok", False), + "summary": output_review_kit.get("summary", {}), + "artifacts": output_review_kit.get("artifacts", {}), + "failures": output_review_kit.get("failures", []), + }, + "output_review_adjudication": { + "ok": output_review_adjudication.get("ok", False), + "summary": output_review_adjudication.get("summary", {}), + "reviewer": output_review_adjudication.get("reviewer", ""), + "reviewed_at": output_review_adjudication.get("reviewed_at", ""), + "failures": output_review_adjudication.get("failures", []), + }, + "benchmark_reproducibility": { + "ok": benchmark_reproducibility.get("ok", False), + "summary": benchmark_reproducibility.get("summary", {}), + "commit": benchmark_reproducibility.get("commit", ""), + "missing_artifacts": benchmark_reproducibility.get("missing_artifacts", []), + "limitations": benchmark_reproducibility.get("limitations", []), + }, + "runtime_conformance": conformance.get("summary", {}), + "runtime_permissions": { + "ok": runtime_permissions.get("ok", False), + "summary": runtime_permissions.get("summary", {}), + "expected_capabilities": runtime_permissions.get("expected_capabilities", []), + "targets": [ + { + "target": item.get("target", ""), + "status": item.get("status", ""), + "assurance": item.get("assurance", ""), + "native_enforcement": item.get("native_enforcement"), + "metadata_fallback_explicit": item.get("metadata_fallback_explicit", False), + "residual_risks": item.get("residual_risks", []), + } + for item in runtime_permissions.get("targets", []) + if isinstance(item, dict) + ], + "failures": runtime_permissions.get("failures", []), + }, + "trust_security": trust_report.get("summary", {}), + "skill_atlas": skill_atlas.get("summary", {}), + "registry_distribution": { + "ok": registry_audit.get("ok", False), + "package": registry_audit.get("package", {}), + "failures": registry_audit.get("failures", []), + "warnings": registry_audit.get("warnings", []), + }, + "package_verification": { + "ok": package_verification.get("ok", False), + "summary": package_verification.get("summary", {}), + "failures": package_verification.get("failures", []), + "warnings": package_verification.get("warnings", []), + }, + "install_simulation": { + "ok": install_simulation.get("ok", False), + "summary": install_simulation.get("summary", {}), + "failures": install_simulation.get("failures", []), + "warnings": install_simulation.get("warnings", []), + }, + "upgrade_check": { + "ok": upgrade_check.get("ok", False), + "summary": upgrade_check.get("summary", {}), + "upgrade_diff": upgrade_check.get("upgrade_diff", {}), + "release_notes": upgrade_check.get("release_notes", []), + "failures": upgrade_check.get("failures", []), + "warnings": upgrade_check.get("warnings", []), + }, + "adoption_drift": { + "ok": adoption_drift.get("ok", False), + "summary": adoption_drift.get("summary", {}), + "next_iteration_candidates": adoption_drift.get("next_iteration_candidates", []), + "privacy_contract": adoption_drift.get("privacy_contract", {}), + "failures": adoption_drift.get("failures", []), + }, + "review_waivers": { + "ok": review_waivers.get("ok", False), + "summary": review_waivers.get("summary", {}), + "policy": review_waivers.get("policy", {}), + "failures": review_waivers.get("failures", []), + "warnings": review_waivers.get("warnings", []), + }, + "review_annotations": { + "ok": review_annotations.get("ok", False), + "summary": review_annotations.get("summary", {}), + "annotations": review_annotations.get("annotations", [])[:8], + "failures": review_annotations.get("failures", []), + }, + "world_class_evidence_plan": { + "ok": world_class_evidence.get("ok", False), + "summary": world_class_evidence.get("summary", {}), + "tasks": world_class_evidence.get("tasks", [])[:8], + "source_audit": world_class_evidence.get("source_audit", {}), + }, + "world_class_evidence_ledger": { + "ok": world_class_evidence_ledger.get("ok", False), + "summary": world_class_evidence_ledger.get("summary", {}), + "entries": world_class_evidence_ledger.get("entries", [])[:8], + "source_plan": world_class_evidence_ledger.get("source_plan", {}), + }, + "synthesis_highlights": synthesis, + "artifact_design": q_review["artifact_design"], + "prompt_quality": q_review["prompt_quality"], + "system_model": q_review["system_model"], + "metadata": metadata, + } + return model diff --git a/scripts/skill_report_sections.py b/scripts/skill_report_sections.py new file mode 100644 index 0000000..d78680c --- /dev/null +++ b/scripts/skill_report_sections.py @@ -0,0 +1,114 @@ +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by skill_report_model.py to assemble overview report quality, risk, and asset sections." + + +def artifact_design_highlights(profile: dict) -> list[str]: + primary = profile.get("primary_artifact", {}) + highlights = [] + if primary.get("direction"): + highlights.append(primary["direction"]) + highlights.extend(profile.get("quality_gates", [])[:3]) + return highlights[:4] + + +def prompt_quality_highlights(profile: dict) -> list[str]: + highlights = [] + primary = profile.get("primary_task_family", {}) + complexity = profile.get("complexity", {}) + if primary.get("label"): + highlights.append(f"Primary prompt task family: {primary['label']}.") + if complexity.get("band"): + highlights.append(f"Complexity: {complexity['band']} — {complexity.get('reason', '')}") + for item in profile.get("quality_matrix", [])[:2]: + highlights.append(f"{item.get('label', 'Quality')}: {item.get('score', 'n/a')}/100.") + return highlights[:4] + + +def system_model_highlights(model: dict) -> list[str]: + highlights = [] + stability = model.get("stability", {}) + if stability: + highlights.append(f"Stability: {stability.get('band', 'unknown')} ({stability.get('score', 'n/a')}/100).") + boundary = model.get("boundary_map", {}) + if boundary.get("owned_job"): + highlights.append(f"Owned job: {boundary['owned_job']}") + for point in model.get("leverage_points", [])[:2]: + if point.get("point"): + highlights.append(f"Leverage: {point['point']} — {point.get('move', '')}") + return highlights[:4] + + +def risk_governance(output_risk: dict, system_model: dict, scorecard: dict) -> dict: + risk_names = [ + ("误触发风险", "trigger_score"), + ("输出漂移风险", "evidence_score"), + ("证据不足风险", "evidence_score"), + ("包体膨胀风险", "maintainability_score"), + ("跨平台迁移风险", "portability_score"), + ] + risks = [] + for index, (name, metric_key) in enumerate(risk_names): + score = scorecard.get(metric_key, {}).get("score", 50) + probability = max(1, min(3, 4 - round(score / 34))) + impact = 3 if index in {0, 2, 4} else 2 + risks.append( + { + "name": name, + "impact": impact, + "probability": probability, + "signal": scorecard.get(metric_key, {}).get("reasons", ["证据不足"])[0], + "response": "先补证据和边界,再增加包体复杂度。", + } + ) + human_boundary = system_model.get("boundary_map", {}).get("human_judgment_boundary", []) + return { + "risks": risks, + "risk_families": output_risk.get("risk_families", []), + "human_judgment_boundary": human_boundary, + } + + +def quality_review( + strengths: list[str], + scorecard: dict, + artifact_design: dict, + prompt_quality: dict, + system_model: dict, +) -> dict: + gaps = [] + for key, payload in scorecard.items(): + if payload.get("score", 0) < 70: + gaps.append(f"{payload.get('label', key)}需要补强:{payload.get('reasons', ['证据不足'])[0]}") + return { + "strengths": strengths, + "gaps": gaps or ["当前关键证据较完整,优先保持轻量。"], + "recommendations": [ + "先改触发边界,再扩展工作流。", + "只把重复且稳定的步骤沉淀为脚本。", + "每次升级后重新生成报告并检查分数原因。", + ], + "artifact_design": { + "design_system": artifact_design.get("design_system", "content-led editorial"), + "highlights": artifact_design_highlights(artifact_design), + }, + "prompt_quality": { + "overall_quality_score": prompt_quality.get("overall_quality_score", "n/a"), + "highlights": prompt_quality_highlights(prompt_quality), + }, + "system_model": { + "stability": system_model.get("stability", {}), + "highlights": system_model_highlights(system_model), + }, + } + + +def package_assets(package_map: list[dict]) -> dict: + files = sum(item.get("file_count", 0) for item in package_map if item.get("kind") == "file") + folders = [item for item in package_map if item.get("kind") == "folder"] + distribution = [{"label": item["path"], "value": max(1, item.get("file_count", 1))} for item in package_map] + return { + "entries": package_map, + "file_count": files + sum(item.get("file_count", 0) for item in folders), + "folder_count": len(folders), + "distribution": distribution, + } diff --git a/scripts/skill_report_sources.py b/scripts/skill_report_sources.py new file mode 100644 index 0000000..961171d --- /dev/null +++ b/scripts/skill_report_sources.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Source loading and package scanning helpers for skill overview reports.""" + +import json +import re +from pathlib import Path + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by skill_report_model.py to load source files and scan overview package assets." + + +KNOWN_ENTRIES = [ + ("SKILL.md", "Skill entrypoint"), + ("README.md", "Human-readable usage guide"), + ("agents/interface.yaml", "Neutral interface metadata"), + ("manifest.json", "Lifecycle and portability metadata"), + ("references", "Extended guidance and reusable notes"), + ("scripts", "Deterministic helpers or local tooling"), + ("evals", "Trigger and quality checks"), + ("reports", "Generated evidence and overview artifacts"), +] + +IGNORED_PACKAGE_PARTS = {".git", "__pycache__", ".venv", "venv", "node_modules", "dist"} + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + data = yaml.safe_load(frontmatter_text) or {} + return data if isinstance(data, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def load_yaml(path: Path) -> dict: + if not path.exists(): + return {} + text = path.read_text(encoding="utf-8") + if yaml is not None: + payload = yaml.safe_load(text) or {} + return payload if isinstance(payload, dict) else {} + return {} + + +def load_json(path: Path) -> dict: + 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 extract_title(body: str, fallback: str) -> str: + for line in body.splitlines(): + if line.startswith("# "): + return line[2:].strip() + return fallback + + +def parse_sections(body: str) -> dict[str, str]: + sections: dict[str, list[str]] = {} + current = "_preamble" + sections[current] = [] + for line in body.splitlines(): + if line.startswith("## "): + current = line[3:].strip() + sections[current] = [] + continue + sections[current].append(line) + return {name: "\n".join(lines).strip() for name, lines in sections.items()} + + +def extract_list_items(text: str) -> list[str]: + items = [] + for line in text.splitlines(): + stripped = line.strip() + if not stripped: + continue + ordered = re.match(r"^\d+\.\s+(.*)$", stripped) + bullet = re.match(r"^[-*]\s+(.*)$", stripped) + match = ordered or bullet + if match: + items.append(match.group(1).strip()) + return items + + +def summarize_logic(sections: dict[str, str]) -> list[str]: + for key in ("Compact Workflow", "Workflow", "How It Works", "Logic", "Quick Start"): + if key in sections: + items = extract_list_items(sections[key]) + if items: + return items[:5] + return extract_list_items(sections.get("_preamble", ""))[:5] or [ + "Understand the request", + "Execute the main task", + "Validate the result", + ] + + +def summarize_usage(sections: dict[str, str], default_prompt: str, description: str) -> list[str]: + for key in ("How To Use", "Quick Start", "Usage", "Runbook"): + if key in sections: + items = extract_list_items(sections[key]) + if items: + return items[:5] + usage = [] + if default_prompt: + usage.append(default_prompt) + usage.append(f"Use this skill when the request matches: {description}") + return usage[:5] + + +def package_entries(skill_dir: Path) -> list[dict]: + items = [] + for rel_path, label in KNOWN_ENTRIES: + target = skill_dir / rel_path + if target.exists(): + kind = "folder" if target.is_dir() else "file" + if target.is_dir(): + count = len( + [ + path + for path in target.rglob("*") + if path.is_file() + and not path.is_symlink() + and not any(part in IGNORED_PACKAGE_PARTS for part in path.relative_to(target).parts) + and path.suffix not in {".pyc", ".pyo"} + ] + ) + else: + count = 1 + items.append({"path": rel_path, "label": label, "kind": kind, "file_count": count}) + return items diff --git a/scripts/skill_report_world_class.py b/scripts/skill_report_world_class.py new file mode 100644 index 0000000..fffcd96 --- /dev/null +++ b/scripts/skill_report_world_class.py @@ -0,0 +1,128 @@ +"""World-class evidence shaping for the static skill overview report.""" + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by skill_report_model.py to summarize world-class evidence readiness and roadmap actions." + +WORLD_CLASS_ENTRY_COPY = { + "provider-holdout": { + "label_zh": "提供商留出", + "label_en": "Provider Holdout", + "summary_zh": "缺少真实 provider 模型运行和 token metadata。", + "summary_en": "Missing a real provider model run and token metadata.", + }, + "human-adjudication": { + "label_zh": "人工盲评", + "label_en": "Human Adjudication", + "summary_zh": "盲评 pair 仍待真实 reviewer 决策。", + "summary_en": "Blind-review pairs still need real reviewer decisions.", + }, + "native-permission-enforcement": { + "label_zh": "原生权限", + "label_en": "Native Permission", + "summary_zh": "原生 runtime enforcement 仍待目标客户端或外部安装器证明。", + "summary_en": "Native runtime enforcement still needs target-client or external-installer proof.", + }, + "native-client-telemetry": { + "label_zh": "原生遥测", + "label_en": "Native Telemetry", + "summary_zh": "真实外部客户端 metadata-only 事件仍未导入。", + "summary_en": "Real external-client metadata-only events have not been imported yet.", + }, +} + + +def world_class_readiness(ledger: dict) -> dict: + summary = ledger.get("summary", {}) if isinstance(ledger, dict) else {} + entries = ledger.get("entries", []) if isinstance(ledger, dict) else [] + if not isinstance(entries, list): + entries = [] + source_check_count = int(summary.get("source_check_count", 0) or 0) + source_pass_count = int(summary.get("source_pass_count", 0) or 0) + pending_count = int(summary.get("pending_count", len(entries)) or 0) + accepted_count = int(summary.get("accepted_count", 0) or 0) + entry_count = int(summary.get("ledger_entry_count", len(entries)) or 0) + ready = bool(summary.get("ready_to_claim_world_class", False)) + decision = str(summary.get("decision", "not-generated" if entry_count == 0 else "evidence-pending")) + if entry_count == 0: + conclusion_zh = "未生成 world-class ledger;当前报告不会宣称世界级完成。" + conclusion_en = "No world-class ledger was generated; this report does not claim world-class completion." + elif ready: + conclusion_zh = "世界级证据已被 ledger 接受,可进入公开 claim 前的最终复核。" + conclusion_en = "World-class evidence is accepted by the ledger and can move to final claim review." + else: + conclusion_zh = f"世界级证据尚未完成:{pending_count} 项待补,{accepted_count} 项已接受。" + conclusion_en = f"World-class evidence is not complete: {pending_count} pending, {accepted_count} accepted." + + items = [] + for entry in entries[:4]: + if not isinstance(entry, dict): + continue + key = str(entry.get("key", "")) + copy = WORLD_CLASS_ENTRY_COPY.get(key, {}) + category = str(entry.get("category", "external")) + checklist = entry.get("source_checklist", []) + blocked_checks = [ + str(check.get("label", "")) + for check in checklist + if isinstance(check, dict) and check.get("status") == "blocked" and str(check.get("label", "")).strip() + ][:3] + items.append( + { + "key": key, + "label_zh": copy.get("label_zh", str(entry.get("label", key))), + "label_en": copy.get("label_en", str(entry.get("label", key))), + "category": category, + "category_zh": "人工证据" if category == "human" else "外部证据", + "category_en": "Human evidence" if category == "human" else "External evidence", + "status": str(entry.get("status", "pending")), + "summary_zh": copy.get("summary_zh", str(entry.get("current", ""))), + "summary_en": copy.get("summary_en", str(entry.get("current", ""))), + "blocked_checks": blocked_checks, + } + ) + return { + "ready": ready, + "decision": decision, + "entry_count": entry_count, + "pending_count": pending_count, + "accepted_count": accepted_count, + "external_pending_count": int(summary.get("external_pending_count", 0) or 0), + "human_pending_count": int(summary.get("human_pending_count", 0) or 0), + "source_check_count": source_check_count, + "source_pass_count": source_pass_count, + "conclusion_zh": conclusion_zh, + "conclusion_en": conclusion_en, + "entries": items, + } + + +def world_class_roadmap_item(readiness: dict) -> dict | None: + if not isinstance(readiness, dict): + return None + pending_count = int(readiness.get("pending_count", 0) or 0) + if readiness.get("ready") or pending_count <= 0: + return None + external_pending_count = int(readiness.get("external_pending_count", 0) or 0) + human_pending_count = int(readiness.get("human_pending_count", 0) or 0) + actions = [] + entries = [entry for entry in readiness.get("entries", []) if isinstance(entry, dict)] + for entry in entries[:2]: + label = str(entry.get("label_zh") or entry.get("key") or "证据项") + summary = str(entry.get("summary_zh") or "仍待补充真实证据。") + actions.append(f"补齐{label}证据:{summary}") + remaining = pending_count - len(actions) + if remaining > 0: + actions.append( + f"按 ledger 总量继续补齐 {pending_count} 项待补证据" + f"(外部 {external_pending_count} 项、人工 {human_pending_count} 项;" + f"未展开 {remaining} 项),并保持 claim guard 为 pending 状态。" + ) + else: + actions.append("提交有效 intake packet,并让 ledger 通过 artifact SHA-256 校验。") + return { + "title": "补齐世界证据", + "why": f"世界级证据仍有 {pending_count} 项待补;公开完成态 claim 必须继续保持阻塞。", + "actions": actions[:3], + "unlocks": "全部外部/人工证据被 ledger 接受后,才能进入公开 world-class claim 复核。", + "source": "world_class_evidence_ledger", + } diff --git a/scripts/skillops_opportunity.py b/scripts/skillops_opportunity.py new file mode 100644 index 0000000..f865702 --- /dev/null +++ b/scripts/skillops_opportunity.py @@ -0,0 +1,214 @@ +from __future__ import annotations + +import hashlib +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_daily_skillops_report.py to score SkillOps opportunities without writing files." + +ACTION_POLICY: dict[str, dict[str, str]] = { + "language_default": { + "action_type": "patch_existing_skill", + "opportunity_type": "report-experience", + "reason": "Repeated language preferences can improve existing report templates after approval.", + }, + "report_ui": { + "action_type": "patch_existing_skill", + "opportunity_type": "artifact-quality", + "reason": "Repeated report layout feedback maps to existing renderer and design doctrine changes.", + }, + "approval_safety": { + "action_type": "agents_update", + "opportunity_type": "governance", + "reason": "Repeated privacy and approval signals should tighten durable operating guidance.", + }, + "delivery_format": { + "action_type": "patch_existing_skill", + "opportunity_type": "artifact-discoverability", + "reason": "Repeated delivery-path requests can improve CLI and README artifact discoverability.", + }, + "evidence_testing": { + "action_type": "add_eval", + "opportunity_type": "quality-gate", + "reason": "Repeated verification and evidence requests should become focused checks.", + }, +} + +RISK_PENALTY = { + "low": 0, + "medium": 8, + "high": 18, +} + + +def _stable_id(pattern_id: str, target_files: list[str], title: str) -> str: + digest = hashlib.sha1(f"{pattern_id}:{title}:{','.join(target_files)}".encode("utf-8")).hexdigest() + return f"skillops-{digest[:10]}" + + +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 _risk_level(proposal: dict[str, Any]) -> str: + risk = str(proposal.get("risk_level") or "medium").lower() + return risk if risk in RISK_PENALTY else "medium" + + +def _target_files(proposal: dict[str, Any]) -> list[str]: + files = proposal.get("target_files") + if not isinstance(files, list): + return [] + return [str(item) for item in files if str(item).strip()] + + +def _verification_commands(proposal: dict[str, Any]) -> list[str]: + commands = proposal.get("verification_commands") + if not isinstance(commands, list): + return [] + return [str(item) for item in commands if str(item).strip()] + + +def _score(pattern: dict[str, Any], proposal: dict[str, Any]) -> tuple[int, list[str]]: + support = _as_int(pattern.get("support_count"), _as_int(proposal.get("support_count"), 0)) + confidence = pattern.get("confidence", 0.5) + try: + confidence_score = int(float(confidence) * 25) + except (TypeError, ValueError): + confidence_score = 12 + target_files = _target_files(proposal) + verification_commands = _verification_commands(proposal) + risk = _risk_level(proposal) + reasons = [ + f"support_count={support}", + f"confidence={confidence}", + f"risk={risk}", + ] + score = 28 + score += min(30, support * 10) + score += max(0, min(25, confidence_score)) + if target_files: + score += 8 + reasons.append("target_files_present") + else: + reasons.append("target_files_missing") + if verification_commands: + score += 7 + reasons.append("verification_present") + else: + reasons.append("verification_missing") + score -= RISK_PENALTY[risk] + if proposal.get("requires_approval") is True: + reasons.append("approval_required") + return max(0, min(100, score)), reasons + + +def _decision(score: int, proposal: dict[str, Any]) -> str: + if score >= 85: + return "ready_for_approval_review" + if score >= 70: + return "proposal_review" + if score >= 50: + return "observe_more_evidence" + if proposal.get("requires_approval") is True: + return "report_only" + return "no_action" + + +def _priority(score: int, risk: str) -> str: + if score >= 85 and risk == "low": + return "high" + if score >= 70: + return "medium" + return "low" + + +def build_opportunities(patterns: list[dict[str, Any]], proposals: list[dict[str, Any]]) -> list[dict[str, Any]]: + pattern_by_id = {str(item.get("pattern_id", "")): item for item in patterns if isinstance(item, dict)} + opportunities: list[dict[str, Any]] = [] + for proposal in proposals: + if not isinstance(proposal, dict): + continue + pattern_id = str(proposal.get("pattern_id") or "generic") + pattern = pattern_by_id.get(pattern_id, {}) + policy = ACTION_POLICY.get( + pattern_id, + { + "action_type": "report_only", + "opportunity_type": "manual-review", + "reason": "Unknown pattern family requires manual review before any durable change.", + }, + ) + target_files = _target_files(proposal) + verification_commands = _verification_commands(proposal) + risk = _risk_level(proposal) + score, score_reasons = _score(pattern, proposal) + evidence_refs = proposal.get("evidence_refs") + if not isinstance(evidence_refs, list): + evidence_refs = [] + opportunities.append( + { + "opportunity_id": _stable_id(pattern_id, target_files, str(proposal.get("title") or "")), + "proposal_id": proposal.get("proposal_id", ""), + "pattern_id": pattern_id, + "title": proposal.get("title", ""), + "opportunity_type": policy["opportunity_type"], + "action_type": policy["action_type"], + "decision": _decision(score, proposal), + "priority": _priority(score, risk), + "score": score, + "score_reasons": score_reasons, + "policy_reason": policy["reason"], + "risk_level": risk, + "requires_approval": proposal.get("requires_approval") is True, + "write_allowed_without_approval": False, + "evidence_count": len(evidence_refs) or _as_int(pattern.get("support_count"), 0), + "target_files": target_files, + "verification_commands": verification_commands, + } + ) + opportunities.sort(key=lambda item: (-int(item["score"]), str(item["opportunity_id"]))) + return opportunities + + +def summarize_opportunities(opportunities: list[dict[str, Any]]) -> dict[str, Any]: + by_action: dict[str, int] = {} + by_decision: dict[str, int] = {} + for item in opportunities: + by_action[str(item.get("action_type", ""))] = by_action.get(str(item.get("action_type", "")), 0) + 1 + by_decision[str(item.get("decision", ""))] = by_decision.get(str(item.get("decision", "")), 0) + 1 + top_score = int(opportunities[0]["score"]) if opportunities else 0 + return { + "opportunity_count": len(opportunities), + "top_score": top_score, + "ready_for_approval_review_count": by_decision.get("ready_for_approval_review", 0), + "proposal_review_count": by_decision.get("proposal_review", 0), + "observe_more_evidence_count": by_decision.get("observe_more_evidence", 0), + "report_only_count": by_decision.get("report_only", 0), + "action_type_counts": by_action, + "decision_counts": by_decision, + } + + +def decision_policy_contract() -> dict[str, Any]: + return { + "schema_version": "1.0", + "contract": "skillops-opportunity-scoring", + "score_range": [0, 100], + "score_bands": { + "85-100": "ready_for_approval_review", + "70-84": "proposal_review", + "50-69": "observe_more_evidence", + "0-49": "report_only_or_no_action", + }, + "action_types": sorted({item["action_type"] for item in ACTION_POLICY.values()} | {"report_only"}), + "writes_source_files": False, + "auto_patch_enabled": False, + "approval_required_for_writes": True, + } diff --git a/scripts/summarize_user_signals.py b/scripts/summarize_user_signals.py new file mode 100644 index 0000000..3ce15e4 --- /dev/null +++ b/scripts/summarize_user_signals.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import re +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT_INTERFACE = "cli" +SCRIPT_INTERFACE_REASON = "Scans an explicit local source file and summarizes redacted repeated user preference signals." + +TEXT_FIELDS = ("text", "message", "content", "excerpt", "prompt", "note", "body") +HISTORY_FILENAMES = {".zsh_history", ".bash_history", ".fish_history", "History"} +SECRET_PATTERNS = [ + re.compile(r"sk-[A-Za-z0-9_-]{12,}"), + re.compile(r"AKIA[0-9A-Z]{12,}"), + re.compile(r"(?i)\b(api[_-]?key|token|password|secret)\b\s*[:=]\s*[^\s,;]+"), +] +EMAIL_RE = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b") +LOCAL_PATH_RE = re.compile(r"/Users/[^\s'\"<>]+") + +PATTERN_RULES = [ + { + "pattern_id": "language_default", + "label": "Default language preference", + "signal_type": "report-language", + "keywords": ["中文", "简体", "默认中文", "英文", "双语", "language", "bilingual", "chinese", "english"], + "recommended_action": "Keep generated reports Chinese-first with an English switch where user-facing.", + }, + { + "pattern_id": "report_ui", + "label": "Report UI and visualization preference", + "signal_type": "artifact-design", + "keywords": ["报告", "html", "图表", "排版", "ui", "kami", "白底", "模块", "导航", "report", "chart", "layout"], + "recommended_action": "Prioritize white-background Kami-style reports with readable charts and stable navigation.", + }, + { + "pattern_id": "approval_safety", + "label": "Approval and privacy boundary", + "signal_type": "governance", + "keywords": ["审批", "授权", "不要扫描", "隐私", "私人", "日志", "明确路径", "回滚", "提案", "批准", "approval", "privacy", "private", "proposal", "rollback"], + "recommended_action": "Keep adaptive work proposal-only until a reviewer approves an allowlisted patch path.", + }, + { + "pattern_id": "delivery_format", + "label": "Delivery format preference", + "signal_type": "artifact-format", + "keywords": ["markdown", "pdf", "word", "docx", "html", "地址", "路径", "打开", "输出", "交付"], + "recommended_action": "Surface stable artifact paths and formats in CLI output and generated summaries.", + }, + { + "pattern_id": "evidence_testing", + "label": "Evidence and testing preference", + "signal_type": "quality-gate", + "keywords": ["测试", "验证", "ci", "证据", "覆盖", "github", "push", "evidence", "review"], + "recommended_action": "Attach focused tests and refreshed evidence reports to every non-trivial skill upgrade.", + }, +] + + +def utc_now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def display_path(path: Path, skill_dir: Path) -> str: + try: + return str(path.resolve().relative_to(skill_dir.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 source_fingerprint(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def redact_text(text: str) -> str: + redacted = text + for pattern in SECRET_PATTERNS: + redacted = pattern.sub("[REDACTED_SECRET]", redacted) + redacted = EMAIL_RE.sub("[REDACTED_EMAIL]", redacted) + redacted = LOCAL_PATH_RE.sub("[LOCAL_PATH]", redacted) + redacted = re.sub(r"\s+", " ", redacted).strip() + if len(redacted) > 240: + return redacted[:237].rstrip() + "..." + return redacted + + +def extract_text(raw: Any) -> str: + if isinstance(raw, str): + return raw + if not isinstance(raw, dict): + return "" + for field in TEXT_FIELDS: + value = raw.get(field) + if isinstance(value, str) and value.strip(): + return value + messages = raw.get("messages") + if isinstance(messages, list): + parts = [] + for item in messages: + if isinstance(item, dict): + content = item.get("content") + if isinstance(content, str): + parts.append(content) + elif isinstance(item, str): + parts.append(item) + return "\n".join(parts) + return "" + + +def load_records(source: Path) -> tuple[list[dict[str, str]], list[str]]: + records: list[dict[str, str]] = [] + failures: list[str] = [] + text = source.read_text(encoding="utf-8", errors="replace") + if source.suffix.lower() == ".jsonl": + for index, line in enumerate(text.splitlines(), start=1): + if not line.strip(): + continue + try: + raw = json.loads(line) + except json.JSONDecodeError as exc: + failures.append(f"line {index}: invalid JSONL source: {exc.msg}") + continue + extracted = extract_text(raw) + if not extracted.strip(): + failures.append(f"line {index}: no supported text field found") + continue + records.append({"record_id": f"line-{index}", "excerpt": redact_text(extracted)}) + else: + for index, line in enumerate(text.splitlines(), start=1): + if line.strip(): + records.append({"record_id": f"line-{index}", "excerpt": redact_text(line)}) + return records, failures + + +def classify_patterns(records: list[dict[str, str]], min_support: int) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + patterns: list[dict[str, Any]] = [] + discarded: list[dict[str, Any]] = [] + for rule in PATTERN_RULES: + matches = [] + for record in records: + lowered = record["excerpt"].lower() + if any(keyword.lower() in lowered for keyword in rule["keywords"]): + matches.append(record) + if not matches: + continue + item = { + "pattern_id": rule["pattern_id"], + "label": rule["label"], + "signal_type": rule["signal_type"], + "support_count": len(matches), + "confidence": min(0.95, round(0.55 + (0.12 * len(matches)), 2)), + "reason": f"{len(matches)} redacted records matched repeated {rule['signal_type']} signals.", + "recommended_action": rule["recommended_action"], + "evidence": matches[:3], + } + if len(matches) >= min_support: + patterns.append(item) + else: + discarded.append({**item, "discard_reason": f"support_count below min_support {min_support}"}) + return patterns, discarded + + +def build_report( + skill_dir: Path, + source: Path, + min_support: int, + generated_at: str, + allow_history_source: bool, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + source = source.resolve() + failures: list[str] = [] + records: list[dict[str, str]] = [] + fingerprint = "" + if not source.exists(): + failures.append(f"Explicit source does not exist: {display_path(source, skill_dir)}") + elif not source.is_file(): + failures.append(f"Explicit source must be a file: {display_path(source, skill_dir)}") + elif source.name in HISTORY_FILENAMES and not allow_history_source: + failures.append(f"Refusing private history source by default: {source.name}") + else: + fingerprint = source_fingerprint(source) + records, load_failures = load_records(source) + failures.extend(load_failures) + patterns, discarded = classify_patterns(records, min_support) if not failures else ([], []) + return { + "ok": not failures, + "schema_version": "1.0", + "generated_at": generated_at, + "skill_dir": display_path(skill_dir, skill_dir), + "source": { + "label": source.name, + "path": display_path(source, skill_dir), + "fingerprint_sha256": fingerprint, + "explicit_source": True, + "record_count": len(records), + }, + "privacy_contract": { + "local_only": True, + "explicit_source_required": True, + "implicit_private_log_scan": False, + "raw_content_stored": False, + "redacted_excerpts_only": True, + "redacted_excerpt_limit": 240, + "writes_repository_files": False, + }, + "summary": { + "record_count": len(records), + "pattern_count": len(patterns), + "discarded_signal_count": len(discarded), + "min_support": min_support, + "failure_count": len(failures), + }, + "patterns": patterns, + "discarded_signals": discarded, + "failures": failures, + "artifacts": { + "json": "reports/user_patterns.json", + "markdown": "reports/user_patterns.md", + }, + } + + +def render_markdown(report: dict[str, Any]) -> str: + lines = [ + "# User Pattern Summary", + "", + f"- Generated at: `{report['generated_at']}`", + f"- Local only: `{str(report['privacy_contract']['local_only']).lower()}`", + f"- Explicit source: `{report['source']['path']}`", + f"- Records: `{report['summary']['record_count']}`", + f"- Patterns: `{report['summary']['pattern_count']}`", + f"- Discarded signals: `{report['summary']['discarded_signal_count']}`", + "", + "## Privacy Contract", + "", + "- No implicit private log scan.", + "- No unredacted raw content stored.", + "- Scan and proposal stages do not write source files.", + "", + "## Patterns", + "", + ] + if not report["patterns"]: + lines.append("- No repeated pattern met the support threshold.") + for pattern in report["patterns"]: + lines.extend( + [ + f"### {pattern['label']}", + "", + f"- Pattern: `{pattern['pattern_id']}`", + f"- Support: `{pattern['support_count']}`", + f"- Confidence: `{pattern['confidence']}`", + f"- Reason: {pattern['reason']}", + f"- Recommended action: {pattern['recommended_action']}", + "- Redacted evidence:", + ] + ) + for item in pattern["evidence"]: + lines.append(f" - `{item['record_id']}`: {item['excerpt']}") + lines.append("") + if report["discarded_signals"]: + lines.extend(["## Discarded Signals", ""]) + for item in report["discarded_signals"]: + lines.append(f"- `{item['pattern_id']}`: {item['discard_reason']}") + 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="Summarize repeated user preference signals from one explicit local source file.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--source", required=True) + parser.add_argument("--output-json", default="reports/user_patterns.json") + parser.add_argument("--output-md", default="reports/user_patterns.md") + parser.add_argument("--min-support", type=int, default=2) + parser.add_argument("--generated-at", default=utc_now()) + parser.add_argument("--allow-history-source", action="store_true") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + report = build_report( + skill_dir, + Path(args.source), + min_support=max(2, args.min_support), + generated_at=args.generated_at, + allow_history_source=args.allow_history_source, + ) + if report["ok"]: + output_json = resolve_output(skill_dir, args.output_json) + output_md = resolve_output(skill_dir, args.output_md) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + report["artifacts"] = { + "json": display_path(output_json, skill_dir), + "markdown": display_path(output_md, skill_dir), + } + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/sync_local_install.py b/scripts/sync_local_install.py new file mode 100644 index 0000000..d18eff4 --- /dev/null +++ b/scripts/sync_local_install.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python3 +import argparse +import json +import shutil +import subprocess +import tempfile +from datetime import date +from pathlib import Path + +from simulate_install import simulate_install + + +ROOT = Path(__file__).resolve().parent.parent +SKILL_NAME = "yao-meta-skill" +SENTINEL_NAME = ".yao-local-install.json" +DEFAULT_INSTALL_DIR = Path.home() / ".agents" / "skills.disabled" / SKILL_NAME +ACTIVE_INSTALL_DIR = Path.home() / ".agents" / "skills" / SKILL_NAME +DEFAULT_PACKAGE_DIR = ROOT / "dist" +ALLOW_UNTRACKED_PREFIXES = { + ".github", + "agents", + "docs", + "evals", + "failures", + "references", + "scripts", + "templates", + "tests", +} +ALLOW_UNTRACKED_ROOT_FILES = { + ".gitignore", + "LICENSE", + "Makefile", + "README.md", + "SKILL.md", + "VERSION", + "manifest.json", + "requirements-ci.txt", +} +PROTECTED_INSTALL_PREFIXES = {".git"} + + +def git_files(root: Path, *args: str) -> list[Path]: + proc = subprocess.run( + ["git", "ls-files", "-z", *args], + cwd=root, + capture_output=True, + check=True, + ) + raw = proc.stdout.decode("utf-8") + return [Path(item) for item in raw.split("\0") if item] + + +def allow_untracked(path: Path) -> bool: + parts = path.parts + if len(parts) == 1: + return path.name in ALLOW_UNTRACKED_ROOT_FILES + return parts[0] in ALLOW_UNTRACKED_PREFIXES + + +def candidate_files(root: Path) -> tuple[list[Path], list[Path]]: + tracked = set(git_files(root)) + untracked = set(git_files(root, "--others", "--exclude-standard")) + allowed_untracked = {path for path in untracked if allow_untracked(path)} + skipped_untracked = sorted(untracked - allowed_untracked) + return sorted(tracked | allowed_untracked), skipped_untracked + + +def resolve_install_dir(raw: str) -> Path: + return Path(raw).expanduser().resolve() + + +def read_frontmatter_name(skill_md: Path) -> str: + if not skill_md.exists(): + return "" + text = skill_md.read_text(encoding="utf-8") + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return "" + for line in lines[1:]: + stripped = line.strip() + if stripped == "---": + break + if stripped.startswith("name:"): + return stripped.split(":", 1)[1].strip().strip("\"'") + return "" + + +def load_sentinel(install_dir: Path) -> dict: + sentinel = install_dir / SENTINEL_NAME + if not sentinel.exists(): + return {} + payload = json.loads(sentinel.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"Invalid install sentinel: {sentinel}") + if payload.get("skill_name") != SKILL_NAME: + raise ValueError(f"Install sentinel is not for {SKILL_NAME}: {sentinel}") + return payload + + +def validate_install_dir(root: Path, install_dir: Path) -> None: + home = Path.home().resolve() + filesystem_root = Path(install_dir.anchor).resolve() + dangerous_exact = { + filesystem_root, + home, + home / ".agents", + home / ".agents" / "skills", + root, + root.parent, + } + if install_dir in dangerous_exact: + raise ValueError(f"Refusing dangerous install directory: {install_dir}") + if install_dir.is_symlink(): + raise ValueError(f"Refusing symlink install directory: {install_dir}") + if not install_dir.exists(): + return + sentinel = load_sentinel(install_dir) + if sentinel: + return + if read_frontmatter_name(install_dir / "SKILL.md") == SKILL_NAME: + return + if install_dir in {DEFAULT_INSTALL_DIR.resolve(), ACTIVE_INSTALL_DIR.resolve()} and not any(install_dir.iterdir()): + return + raise ValueError( + f"Refusing to sync into a directory that is not a managed {SKILL_NAME} install: {install_dir}" + ) + + +def write_sentinel(root: Path, install_dir: Path, dry_run: bool) -> None: + if dry_run: + return + payload = { + "managed_by": "yao-meta-skill sync_local_install.py", + "skill_name": SKILL_NAME, + "source_root": str(root), + } + (install_dir / SENTINEL_NAME).write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + +def install_preflight(root: Path, package_dir: Path, generated_at: str) -> dict: + package_dir = package_dir.resolve() + with tempfile.TemporaryDirectory(prefix="yao-local-install-preflight-") as temp_dir: + report = simulate_install(root, package_dir, Path(temp_dir), generated_at) + summary = report.get("summary", {}) if isinstance(report.get("summary"), dict) else {} + permission_failures = int(summary.get("installer_permission_failure_count", 0) or 0) + if not report.get("ok") or permission_failures: + failure_preview = "; ".join(str(item) for item in report.get("failures", [])[:3]) + if permission_failures and not failure_preview: + failure_preview = f"{permission_failures} installer permission failures" + raise ValueError(f"Install preflight failed for {package_dir}: {failure_preview or 'unknown failure'}") + return { + "ok": True, + "package_dir": str(package_dir), + "archive_extracted": bool(summary.get("archive_extracted")), + "adapter_count": int(summary.get("adapter_count", 0) or 0), + "installer_permission_enforced_count": int(summary.get("installer_permission_enforced_count", 0) or 0), + "installer_permission_failure_count": permission_failures, + "permission_target_count": int(summary.get("permission_target_count", 0) or 0), + "permission_capability_count": int(summary.get("permission_capability_count", 0) or 0), + "failure_count": int(summary.get("failure_count", 0) or 0), + "warning_count": int(summary.get("warning_count", 0) or 0), + } + + +def remove_stale_files(install_dir: Path, desired_files: set[Path], dry_run: bool) -> list[str]: + removed = [] + if not install_dir.exists(): + return removed + for path in sorted(install_dir.rglob("*"), reverse=True): + rel = path.relative_to(install_dir) + if rel.parts and rel.parts[0] in PROTECTED_INSTALL_PREFIXES: + continue + if path.is_dir(): + continue + if rel not in desired_files: + removed.append(str(rel)) + if not dry_run: + path.unlink() + for path in sorted((item for item in install_dir.rglob("*") if item.is_dir()), reverse=True): + rel = path.relative_to(install_dir) + if rel.parts and rel.parts[0] in PROTECTED_INSTALL_PREFIXES: + continue + try: + if not dry_run: + path.rmdir() + except OSError: + pass + return removed + + +def copy_files(root: Path, install_dir: Path, files: list[Path], dry_run: bool) -> tuple[list[str], list[str]]: + copied = [] + skipped = [] + for rel in files: + source = root / rel + target = install_dir / rel + if source.is_symlink() or not source.is_file(): + skipped.append(str(rel)) + continue + copied.append(str(rel)) + if dry_run: + continue + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(source, target) + return copied, skipped + + +def sync_local_install( + root: Path, + install_dir: Path, + dry_run: bool = False, + package_dir: Path | None = None, + generated_at: str | None = None, + skip_install_preflight: bool = False, +) -> dict: + root = root.resolve() + install_dir = install_dir.resolve() + validate_install_dir(root, install_dir) + preflight = {"ok": True, "skipped": True} + if not skip_install_preflight: + resolved_package_dir = (package_dir or DEFAULT_PACKAGE_DIR).resolve() + preflight = install_preflight(root, resolved_package_dir, generated_at or str(date.today())) + files, skipped_untracked = candidate_files(root) + desired_files = set(files) + desired_files.add(Path(SENTINEL_NAME)) + removed = remove_stale_files(install_dir, desired_files, dry_run) + if not dry_run: + install_dir.mkdir(parents=True, exist_ok=True) + copied, skipped_sources = copy_files(root, install_dir, files, dry_run) + write_sentinel(root, install_dir, dry_run) + return { + "ok": True, + "root": str(root), + "install_dir": str(install_dir), + "dry_run": dry_run, + "copied_count": len(copied), + "removed_count": len(removed), + "skipped_source_count": len(skipped_sources), + "skipped_untracked_count": len(skipped_untracked), + "install_preflight": preflight, + "copied_samples": copied[:10], + "removed_samples": removed[:10], + "skipped_source_samples": skipped_sources[:10], + "skipped_untracked_samples": [str(path) for path in skipped_untracked[:10]], + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Sync the current yao-meta-skill source into a managed local skill mirror.") + parser.add_argument("--root", default=str(ROOT)) + parser.add_argument("--install-dir", default=str(DEFAULT_INSTALL_DIR)) + parser.add_argument("--package-dir", default=str(DEFAULT_PACKAGE_DIR)) + parser.add_argument("--generated-at", default=str(date.today())) + parser.add_argument("--skip-install-preflight", action="store_true") + parser.add_argument("--dry-run", action="store_true") + args = parser.parse_args() + root = Path(args.root).resolve() + package_dir = Path(args.package_dir) + if not package_dir.is_absolute(): + package_dir = root / package_dir + try: + result = sync_local_install( + root, + resolve_install_dir(args.install_dir), + dry_run=args.dry_run, + package_dir=package_dir, + generated_at=args.generated_at, + skip_install_preflight=args.skip_install_preflight, + ) + except (OSError, subprocess.CalledProcessError, ValueError) as exc: + result = {"ok": False, "failures": [str(exc)]} + print(json.dumps(result, ensure_ascii=False, indent=2)) + raise SystemExit(2) from exc + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/telemetry_native_host.py b/scripts/telemetry_native_host.py new file mode 100644 index 0000000..f352055 --- /dev/null +++ b/scripts/telemetry_native_host.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import shlex +import stat +import struct +import sys +from pathlib import Path +from typing import Any, BinaryIO + +from emit_telemetry_event import append_event, default_spool_path +from render_adoption_drift_report import display_path, normalize_event, skill_defaults + + +SCHEMA_VERSION = "1.0" +DEFAULT_HOST_NAME = "com.yao.meta_skill.telemetry" +DEFAULT_DESCRIPTION = "Yao metadata-only telemetry native messaging host" +SKILL_DIR_ENV = "YAO_TELEMETRY_SKILL_DIR" +EVENTS_ENV = "YAO_TELEMETRY_EVENTS" +MAX_MESSAGE_BYTES = 1024 * 1024 + + +def read_native_message(stream: BinaryIO) -> dict[str, Any] | None: + header = stream.read(4) + if not header: + return None + if len(header) != 4: + raise ValueError("native message header must be exactly 4 bytes") + size = struct.unpack(" MAX_MESSAGE_BYTES: + raise ValueError(f"native message is too large: {size} bytes") + payload = stream.read(size) + if len(payload) != size: + raise ValueError("native message payload ended before declared length") + message = json.loads(payload.decode("utf-8")) + if not isinstance(message, dict): + raise ValueError("native message must be a JSON object") + return message + + +def write_native_message(stream: BinaryIO, payload: dict[str, Any]) -> None: + data = json.dumps(payload, ensure_ascii=False, sort_keys=True).encode("utf-8") + stream.write(struct.pack(" dict[str, Any]: + raw = dict(raw_message) + raw.setdefault("source", "external") + raw.setdefault("command", "native-messaging-host") + event, failures = normalize_event(raw, skill_defaults(skill_dir), "native-host-message") + if event and not failures and not dry_run: + append_event(output_jsonl, event) + return { + "ok": not failures, + "schema_version": SCHEMA_VERSION, + "mode": "native-messaging-host", + "skill_dir": display_path(skill_dir), + "output_jsonl": display_path(output_jsonl), + "dry_run": dry_run, + "emitted": bool(event and not failures and not dry_run), + "event": event or {}, + "failures": failures, + } + + +def write_launcher(path: Path, host_script: Path, skill_dir: Path, output_jsonl: Path) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + script = "\n".join( + [ + "#!/bin/sh", + "set -eu", + ( + "exec python3 " + f"{shlex.quote(str(host_script.resolve()))} " + f"{shlex.quote(str(skill_dir.resolve()))} " + "--stdio " + f"--output-jsonl {shlex.quote(str(output_jsonl.resolve()))}" + ), + "", + ] + ) + path.write_text(script, encoding="utf-8") + current = path.stat().st_mode + path.chmod(current | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + + +def write_manifest( + manifest_path: Path, + host_name: str, + description: str, + host_path: Path, + allowed_origins: list[str], +) -> dict[str, Any]: + failures: list[str] = [] + if not allowed_origins: + failures.append("At least one --allowed-origin is required for a Chrome native messaging manifest.") + for origin in allowed_origins: + if not origin.startswith("chrome-extension://") or not origin.endswith("/"): + failures.append(f"Unsupported Chrome extension origin: {origin}") + if not host_path.exists(): + failures.append(f"Native host path does not exist: {host_path}") + elif not os.access(host_path, os.X_OK): + failures.append(f"Native host path must be executable: {host_path}") + manifest = { + "name": host_name, + "description": description, + "path": str(host_path.resolve()), + "type": "stdio", + "allowed_origins": allowed_origins, + } + if not failures: + manifest_path.parent.mkdir(parents=True, exist_ok=True) + manifest_path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return { + "ok": not failures, + "schema_version": SCHEMA_VERSION, + "manifest": manifest, + "manifest_path": display_path(manifest_path), + "failures": failures, + } + + +def run_stdio(skill_dir: Path, output_jsonl: Path, dry_run: bool) -> int: + while True: + raw = read_native_message(sys.stdin.buffer) + if raw is None: + return 0 + response = emit_native_message(skill_dir, output_jsonl, raw, dry_run=dry_run) + write_native_message(sys.stdout.buffer, response) + + +def env_default(name: str, fallback: str) -> str: + return os.environ.get(name) or fallback + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run a metadata-only telemetry native messaging host.") + parser.add_argument("skill_dir", nargs="?", default=env_default(SKILL_DIR_ENV, ".")) + parser.add_argument("--output-jsonl", default=os.environ.get(EVENTS_ENV)) + parser.add_argument("--message-json", help="Emit one JSON object directly, useful for host smoke tests.") + parser.add_argument("--stdio", action="store_true", help="Use Chrome/Browser Native Messaging length-prefixed stdio.") + parser.add_argument("--dry-run", action="store_true") + parser.add_argument("--write-manifest") + parser.add_argument("--write-launcher") + parser.add_argument("--host-name", default=DEFAULT_HOST_NAME) + parser.add_argument("--description", default=DEFAULT_DESCRIPTION) + parser.add_argument("--allowed-origin", action="append", default=[]) + parser.add_argument("--host-path") + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + output_jsonl = Path(args.output_jsonl).resolve() if args.output_jsonl else default_spool_path(skill_dir).resolve() + host_script = Path(__file__).resolve() + + launcher_path = Path(args.write_launcher).resolve() if args.write_launcher else None + if launcher_path: + write_launcher(launcher_path, host_script, skill_dir, output_jsonl) + + if args.write_manifest: + host_path = Path(args.host_path).resolve() if args.host_path else launcher_path or host_script + report = write_manifest( + Path(args.write_manifest).resolve(), + args.host_name, + args.description, + host_path, + args.allowed_origin, + ) + if launcher_path: + report["launcher_path"] = display_path(launcher_path) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + return + + if args.stdio: + try: + raise SystemExit(run_stdio(skill_dir, output_jsonl, args.dry_run)) + except Exception as exc: + write_native_message( + sys.stdout.buffer, + { + "ok": False, + "schema_version": SCHEMA_VERSION, + "mode": "native-messaging-host", + "emitted": False, + "failures": [str(exc)], + }, + ) + raise SystemExit(2) + + if args.message_json: + try: + message = json.loads(args.message_json) + except json.JSONDecodeError as exc: + print(json.dumps({"ok": False, "failures": [f"Invalid --message-json: {exc.msg}"]}, ensure_ascii=False, indent=2)) + raise SystemExit(2) + if not isinstance(message, dict): + print(json.dumps({"ok": False, "failures": ["--message-json must decode to a JSON object"]}, ensure_ascii=False, indent=2)) + raise SystemExit(2) + report = emit_native_message(skill_dir, output_jsonl, message, dry_run=args.dry_run) + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + return + + print( + json.dumps( + { + "ok": False, + "schema_version": SCHEMA_VERSION, + "failures": ["Use --message-json, --stdio, or --write-manifest."], + }, + ensure_ascii=False, + indent=2, + ) + ) + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/trigger_eval.py b/scripts/trigger_eval.py new file mode 100644 index 0000000..ff610af --- /dev/null +++ b/scripts/trigger_eval.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path + + +WORD_RE = re.compile(r"[\w\u4e00-\u9fff]+", re.UNICODE) +DEFAULT_CONFIG_PATH = Path("evals/semantic_config.json") + + +def normalize(text: str) -> str: + text = text.lower() + text = re.sub(r"[^\w\u4e00-\u9fff]+", " ", text, flags=re.UNICODE) + return re.sub(r"\s+", " ", text).strip() + + +def words(text: str) -> set[str]: + return {w.lower() for w in WORD_RE.findall(text)} + + +def load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def extract_description(text: str) -> str: + if not text.startswith("---"): + return text + parts = text.split("---", 2) + if len(parts) < 3: + return text + frontmatter = parts[1].splitlines() + for line in frontmatter: + if line.strip().startswith("description:"): + return line.split(":", 1)[1].strip().strip("'\"") + return text + + +def iter_case_items(cases: dict, bucket: str) -> list[dict]: + items = [] + for raw in cases.get(bucket, []): + if isinstance(raw, str): + items.append({"text": raw, "family": "default"}) + else: + item = dict(raw) + item.setdefault("family", "default") + items.append(item) + return items + + +def phrase_present(text: str, phrase: str) -> bool: + phrase = normalize(phrase) + if not phrase: + return False + if re.search(r"[\u4e00-\u9fff]", phrase): + return phrase in text + return f" {phrase} " in f" {text} " + + +def load_semantic_config(path: Path | None) -> dict: + config_path = path or DEFAULT_CONFIG_PATH + if not config_path.exists(): + raise SystemExit(f"Semantic config not found: {config_path}") + return load_json(config_path) + + +def collect_concept_hits(text: str, concepts: dict[str, dict]) -> dict[str, dict]: + normalized = normalize(text) + hits: dict[str, dict] = {} + for name, spec in concepts.items(): + matched = [] + for phrase in spec.get("phrases", []): + if phrase_present(normalized, phrase): + matched.append(phrase) + if matched: + hits[name] = { + "weight": spec["weight"], + "matched_phrases": matched, + "exclusive": bool(spec.get("exclusive")), + } + return hits + + +def lexical_support(description_words: set[str], prompt: str) -> float: + prompt_words = words(prompt) + if not prompt_words: + return 0.0 + return len(description_words & prompt_words) / len(prompt_words) + + +def desired_positive_concepts(description: str, config: dict) -> list[str]: + description_hits = collect_concept_hits(description, config["positive_concepts"]) + names = list(description_hits) + if names: + return names + return config.get("fallback_positive_concepts", []) + + +def score_prompt_semantic(description: str, prompt: str, config: dict) -> tuple[float, dict]: + positive_concepts = config["positive_concepts"] + negative_concepts = config["negative_concepts"] + desired = desired_positive_concepts(description, config) + desired_weight_total = sum(positive_concepts[name]["weight"] for name in desired) or 1.0 + + prompt_positive_hits = collect_concept_hits(prompt, positive_concepts) + prompt_negative_hits = collect_concept_hits(prompt, negative_concepts) + + matched_desired = sorted([name for name in desired if name in prompt_positive_hits]) + extra_positive = sorted([name for name in prompt_positive_hits if name not in matched_desired]) + semantic_coverage = sum(positive_concepts[name]["weight"] for name in matched_desired) / desired_weight_total + support_score = sum(positive_concepts[name]["weight"] for name in extra_positive) + + exclusive_negative = sorted([name for name, hit in prompt_negative_hits.items() if hit["exclusive"]]) + negative_penalty = sum(hit["weight"] for hit in prompt_negative_hits.values()) + lexical = lexical_support(words(description), prompt) + + coverage_boost = 0.0 + if len(matched_desired) >= 2: + coverage_boost += 0.04 + if len(matched_desired) >= 3: + coverage_boost += 0.02 + + score = (semantic_coverage * 0.92) + min(0.12, support_score * 0.25) + min(0.06, lexical * 0.08) + coverage_boost + score -= negative_penalty + if exclusive_negative and semantic_coverage < 0.9: + score -= 0.15 + score = max(0.0, min(1.0, score)) + + score_detail = { + "mode": "semantic-intent", + "desired_positive_concepts": desired, + "matched_desired_concepts": matched_desired, + "extra_positive_concepts": extra_positive, + "matched_negative_concepts": sorted(prompt_negative_hits), + "exclusive_negative_concepts": exclusive_negative, + "semantic_coverage": round(semantic_coverage, 3), + "support_score": round(support_score, 3), + "lexical_support": round(lexical, 3), + "negative_penalty": round(negative_penalty, 3), + "coverage_boost": round(coverage_boost, 3), + "concept_evidence": { + "positive": { + name: prompt_positive_hits[name]["matched_phrases"] + for name in sorted(prompt_positive_hits) + }, + "negative": { + name: prompt_negative_hits[name]["matched_phrases"] + for name in sorted(prompt_negative_hits) + }, + }, + } + return score, score_detail + + +def classify_bucket(bucket: str) -> bool: + return bucket == "should_trigger" + + +def evaluate(description: str, cases: dict, threshold: float, config: dict) -> dict: + results = {"should_trigger": [], "should_not_trigger": [], "near_neighbor": []} + fp = 0 + fn = 0 + bucket_stats = {} + family_stats: dict[str, dict] = {} + misfires = [] + + for bucket in ("should_trigger", "should_not_trigger", "near_neighbor"): + expected = classify_bucket(bucket) + items = iter_case_items(cases, bucket) + total = 0 + passed_count = 0 + for item in items: + prompt = item["text"] + family = item.get("family", "default") + score, score_detail = score_prompt_semantic(description, prompt, config) + predicted = score >= threshold + passed = predicted == expected + total += 1 + if passed: + passed_count += 1 + if not passed and expected: + fn += 1 + if not passed and not expected: + fp += 1 + + record = { + "prompt": prompt, + "family": family, + "score": round(score, 3), + "predicted_trigger": predicted, + "expected_trigger": expected, + "passed": passed, + "score_detail": score_detail, + } + if 0.75 * threshold <= score <= 1.25 * threshold: + record["boundary_case"] = True + results[bucket].append(record) + + family_bucket = family_stats.setdefault( + family, + {"total": 0, "passed": 0, "false_positives": 0, "false_negatives": 0}, + ) + family_bucket["total"] += 1 + if passed: + family_bucket["passed"] += 1 + elif expected: + family_bucket["false_negatives"] += 1 + else: + family_bucket["false_positives"] += 1 + + if not passed: + misfires.append( + { + "bucket": bucket, + "family": family, + "prompt": prompt, + "score": round(score, 3), + "reason": "false_negative" if expected else "false_positive", + "matched_desired_concepts": score_detail["matched_desired_concepts"], + "matched_negative_concepts": score_detail["matched_negative_concepts"], + } + ) + + bucket_stats[bucket] = { + "total": total, + "passed": passed_count, + "pass_rate": round(passed_count / total, 3) if total else None, + } + + for family, stats in family_stats.items(): + stats["pass_rate"] = round(stats["passed"] / stats["total"], 3) if stats["total"] else None + + tp = sum(1 for item in results["should_trigger"] if item["predicted_trigger"]) + precision = tp / (tp + fp) if (tp + fp) else None + recall = tp / (tp + fn) if (tp + fn) else None + + return { + "threshold": threshold, + "threshold_explanation": ( + "Prompts at or above the threshold are treated as trigger matches. " + "Scores are driven primarily by semantic intent coverage: packaging intent, " + "workflow-to-skill transformation intent, reuse/distribution intent, and eval intent. " + "Explicit exclusions such as summary-only, translation-only, one-off, document-only, " + "or do-not-build directives apply direct penalties and can override otherwise similar wording." + ), + "false_positives": fp, + "false_negatives": fn, + "precision": round(precision, 3) if precision is not None else None, + "recall": round(recall, 3) if recall is not None else None, + "bucket_stats": bucket_stats, + "family_stats": family_stats, + "misfires": misfires, + "results": results, + } + + +def compare_reports(baseline: dict, improved: dict) -> dict: + return { + "baseline_false_positives": baseline["false_positives"], + "baseline_false_negatives": baseline["false_negatives"], + "improved_false_positives": improved["false_positives"], + "improved_false_negatives": improved["false_negatives"], + "false_positive_delta": improved["false_positives"] - baseline["false_positives"], + "false_negative_delta": improved["false_negatives"] - baseline["false_negatives"], + "baseline_precision": baseline["precision"], + "improved_precision": improved["precision"], + "baseline_recall": baseline["recall"], + "improved_recall": improved["recall"], + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Semantic trigger quality evaluator.") + parser.add_argument("--description", help="Description string to evaluate") + parser.add_argument("--description-file", help="Read description text from file") + parser.add_argument("--baseline-description", help="Baseline description string to compare against") + parser.add_argument("--baseline-description-file", help="Read baseline description from file") + parser.add_argument("--cases", required=True, help="JSON file with trigger cases") + parser.add_argument("--semantic-config", default=str(DEFAULT_CONFIG_PATH), help="Semantic config JSON") + parser.add_argument("--threshold", type=float, default=None, help="Trigger threshold override") + args = parser.parse_args() + + description = args.description + if args.description_file: + description = extract_description(Path(args.description_file).read_text(encoding="utf-8")) + if not description: + raise SystemExit("Provide --description or --description-file") + + cases = load_json(Path(args.cases)) + config = load_semantic_config(Path(args.semantic_config)) + threshold = args.threshold if args.threshold is not None else cases.get("recommended_threshold", 0.48) + report = evaluate(description, cases, threshold, config) + + baseline = args.baseline_description + if args.baseline_description_file: + baseline = extract_description(Path(args.baseline_description_file).read_text(encoding="utf-8")) + if baseline: + report["comparison"] = compare_reports(evaluate(baseline, cases, threshold, config), report) + + print(json.dumps(report, ensure_ascii=False, indent=2)) + if report["false_positives"] > 0 or report["false_negatives"] > 0: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/trust_check.py b/scripts/trust_check.py new file mode 100644 index 0000000..8d03125 --- /dev/null +++ b/scripts/trust_check.py @@ -0,0 +1,582 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import subprocess +import sys +import re +from datetime import date +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + +from trust_check_scripts import INTERNAL_SCRIPT_INTERFACE, script_inventory + + +ROOT = Path(__file__).resolve().parent.parent +SCAN_DIRS = ["agents", "assets", "docs", "evals", "references", "runtime", "scripts", "security", "skill-ir", "templates"] +ROOT_FILES = ["SKILL.md", "README.md", "manifest.json", "requirements-ci.txt", "Makefile"] +TEXT_SUFFIXES = {".css", ".js", ".md", ".json", ".jsonl", ".yaml", ".yml", ".py", ".sh", ".txt", ".toml"} +PACKAGE_HASH_SCOPE = "source-contract-without-generated-reports" +NETWORK_POLICY_REL_PATH = "security/network_policy.json" +PERMISSION_POLICY_REL_PATH = "security/permission_policy.json" +HELP_SMOKE_TIMEOUT_SECONDS = 5.0 +PERMISSION_CAPABILITIES = ("network", "file_write", "subprocess", "interactive") +PERMISSION_TARGETS = ("openai", "claude", "generic", "vscode") +SECRET_PATTERNS = [ + ("private_key", re.compile(r"-----BEGIN (?:RSA |DSA |EC |OPENSSH |PGP )?PRIVATE KEY-----")), + ("github_token", re.compile(r"\bgh[pousr]_[A-Za-z0-9_]{20,}\b")), + ("slack_token", re.compile(r"\bxox[baprs]-[A-Za-z0-9-]{20,}\b")), + ("aws_access_key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")), + ("openai_api_key", re.compile(r"\bsk-[A-Za-z0-9]{32,}\b")), +] + + +def display_path(path: Path) -> str: + try: + return str(path.resolve().relative_to(ROOT.resolve())) + except ValueError: + return str(path.resolve()) + + +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 iter_scan_files(skill_dir: Path) -> list[Path]: + files = [] + for rel in ROOT_FILES: + path = skill_dir / rel + if path.exists() and path.is_file(): + files.append(path) + for rel in SCAN_DIRS: + folder = skill_dir / rel + if not folder.exists(): + continue + for path in sorted(folder.rglob("*")): + if not path.is_file() or path.is_symlink(): + continue + if path.suffix in TEXT_SUFFIXES and path.stat().st_size <= 1_000_000: + files.append(path) + return sorted(set(files)) + + +def relpath(skill_dir: Path, path: Path) -> str: + return str(path.relative_to(skill_dir)) + + +def scan_secrets(skill_dir: Path, files: list[Path]) -> list[dict[str, Any]]: + findings = [] + for path in files: + text = path.read_text(encoding="utf-8", errors="replace") + for name, pattern in SECRET_PATTERNS: + for match in pattern.finditer(text): + line = text.count("\n", 0, match.start()) + 1 + findings.append({"type": name, "path": relpath(skill_dir, path), "line": line}) + return findings + + +def dependency_status(skill_dir: Path) -> dict[str, Any]: + candidates = ["requirements-ci.txt", "requirements.txt", "pyproject.toml", "package-lock.json", "uv.lock", "poetry.lock"] + present = [name for name in candidates if (skill_dir / name).exists()] + pinned = [] + unpinned = [] + requirements = skill_dir / "requirements-ci.txt" + if requirements.exists(): + for line in requirements.read_text(encoding="utf-8").splitlines(): + stripped = line.strip() + if not stripped or stripped.startswith("#"): + continue + if "==" in stripped or " @ " in stripped: + pinned.append(stripped) + else: + unpinned.append(stripped) + return {"present": present, "pinned": pinned, "unpinned": unpinned} + + +def script_capability_paths(scripts: list[dict[str, Any]]) -> dict[str, list[str]]: + return { + "network": [item["path"] for item in scripts if item["uses_network"]], + "file_write": [item["path"] for item in scripts if item.get("uses_file_write", False)], + "subprocess": [item["path"] for item in scripts if item["uses_subprocess"]], + "interactive": [item["path"] for item in scripts if item["uses_input"]], + } + + +def load_network_policy(skill_dir: Path) -> dict[str, Any]: + path = skill_dir / NETWORK_POLICY_REL_PATH + if not path.exists(): + return {"present": False, "path": NETWORK_POLICY_REL_PATH, "scripts": {}} + payload = load_json(path) + scripts = payload.get("scripts", {}) + return { + "present": True, + "path": NETWORK_POLICY_REL_PATH, + "schema_version": payload.get("schema_version", ""), + "default_policy": payload.get("default_policy", {}), + "scripts": scripts if isinstance(scripts, dict) else {}, + } + + +def load_permission_policy(skill_dir: Path) -> dict[str, Any]: + path = skill_dir / PERMISSION_POLICY_REL_PATH + if not path.exists(): + return {"present": False, "path": PERMISSION_POLICY_REL_PATH, "capabilities": {}} + payload = load_json(path) + capabilities = payload.get("capabilities", {}) + return { + "present": True, + "path": PERMISSION_POLICY_REL_PATH, + "schema_version": payload.get("schema_version", ""), + "reviewed_at": payload.get("reviewed_at", ""), + "capabilities": capabilities if isinstance(capabilities, dict) else {}, + } + + +def parse_iso_date(value: Any) -> date | None: + try: + return date.fromisoformat(str(value)[:10]) + except (TypeError, ValueError): + return None + + +def permission_governance_status(skill_dir: Path, scripts: list[dict[str, Any]], today: date | None = None) -> dict[str, Any]: + policy = load_permission_policy(skill_dir) + today = today or date.today() + capability_paths = script_capability_paths(scripts) + required = {name: paths for name, paths in capability_paths.items() if paths} + approvals = [] + missing = [] + invalid = [] + expired = [] + approved = [] + + for name, paths in required.items(): + entry = policy["capabilities"].get(name, {}) if policy["present"] else {} + if not isinstance(entry, dict) or not entry: + missing.append(name) + approvals.append({"capability": name, "status": "missing", "scripts": paths, "validation": ["approval entry is missing"]}) + continue + validation = [] + if entry.get("decision") != "approved": + validation.append("decision must be approved") + if not entry.get("reviewer"): + validation.append("reviewer is required") + if len(str(entry.get("reason", ""))) < 30: + validation.append("reason must be at least 30 characters") + if not entry.get("scope"): + validation.append("scope is required") + expires_at = parse_iso_date(entry.get("expires_at")) + if expires_at is None: + validation.append("expires_at must be ISO date") + target_enforcement = entry.get("target_enforcement", {}) + if not isinstance(target_enforcement, dict): + validation.append("target_enforcement must be an object") + target_enforcement = {} + missing_targets = [target for target in PERMISSION_TARGETS if not target_enforcement.get(target)] + if missing_targets: + validation.append(f"target_enforcement missing: {', '.join(missing_targets)}") + if validation: + invalid.append(name) + status = "invalid" + elif expires_at is not None and expires_at < today: + expired.append(name) + status = "expired" + else: + approved.append(name) + status = "approved" + approvals.append( + { + "capability": name, + "status": status, + "scripts": paths, + "reviewer": str(entry.get("reviewer", "")), + "scope": str(entry.get("scope", "")), + "reason": str(entry.get("reason", "")), + "expires_at": str(entry.get("expires_at", "")), + "evidence": entry.get("evidence", []) if isinstance(entry.get("evidence", []), list) else [], + "target_enforcement": target_enforcement, + "validation": validation, + } + ) + + return { + "present": policy["present"], + "path": policy["path"], + "schema_version": policy.get("schema_version", ""), + "reviewed_at": policy.get("reviewed_at", ""), + "required_capabilities": sorted(required), + "approved_capabilities": sorted(approved), + "missing_capabilities": sorted(missing), + "invalid_capabilities": sorted(invalid), + "expired_capabilities": sorted(expired), + "approval_count": len(approved), + "required_count": len(required), + "missing_count": len(missing), + "invalid_count": len(invalid), + "expired_count": len(expired), + "approvals": approvals, + } + + +def network_policy_status(skill_dir: Path, scripts: list[dict[str, Any]]) -> dict[str, Any]: + policy = load_network_policy(skill_dir) + network_scripts = [item for item in scripts if item["uses_network"]] + covered = [] + missing = [] + mismatches = [] + for item in network_scripts: + entry = policy["scripts"].get(item["path"], {}) if policy["present"] else {} + if not isinstance(entry, dict) or not entry: + missing.append(item["path"]) + continue + allowed_hosts = set(entry.get("allowed_hosts") or []) + if not allowed_hosts: + mismatches.append( + { + "path": item["path"], + "reason": "allowed_hosts is empty", + "observed_hosts": item["network_hosts"], + "allowed_hosts": [], + } + ) + continue + observed_hosts = set(item.get("network_hosts") or []) + unexpected_hosts = sorted(observed_hosts - allowed_hosts) + if unexpected_hosts: + mismatches.append( + { + "path": item["path"], + "reason": "observed HTTPS hosts are not declared in policy", + "observed_hosts": sorted(observed_hosts), + "allowed_hosts": sorted(allowed_hosts), + "unexpected_hosts": unexpected_hosts, + } + ) + continue + covered.append(item["path"]) + return { + "present": policy["present"], + "path": policy["path"], + "schema_version": policy.get("schema_version", ""), + "network_script_count": len(network_scripts), + "covered_scripts": covered, + "missing_scripts": missing, + "mismatches": mismatches, + "default_policy": policy.get("default_policy", {}), + } + + +def interface_trust(skill_dir: Path) -> dict[str, Any]: + interface = load_yaml(skill_dir / "agents" / "interface.yaml") + trust = interface.get("compatibility", {}).get("trust", {}) + return { + "source_tier": trust.get("source_tier", ""), + "remote_inline_execution": trust.get("remote_inline_execution", ""), + "remote_metadata_policy": trust.get("remote_metadata_policy", ""), + } + + +def package_digest(files: list[Path], skill_dir: Path) -> str: + digest = hashlib.sha256() + for path in files: + rel = relpath(skill_dir, path) + digest.update(rel.encode("utf-8")) + digest.update(b"\0") + digest.update(hashlib.sha256(path.read_bytes()).hexdigest().encode("ascii")) + digest.update(b"\0") + return digest.hexdigest() + + +def help_smoke_candidates(scripts: list[dict[str, Any]]) -> list[dict[str, Any]]: + return [ + item + for item in scripts + if item["interface"] != INTERNAL_SCRIPT_INTERFACE and item["has_argparse"] + ] + + +def run_help_smoke_checks(skill_dir: Path, scripts: list[dict[str, Any]], timeout: float) -> dict[str, Any]: + candidates = help_smoke_candidates(scripts) + skipped = [ + { + "path": item["path"], + "reason": "internal module" if item["interface"] == INTERNAL_SCRIPT_INTERFACE else "missing argparse/help surface", + } + for item in scripts + if item not in candidates + ] + results = [] + for item in candidates: + command = [sys.executable, item["path"], "--help"] + try: + proc = subprocess.run( + command, + cwd=skill_dir, + capture_output=True, + text=True, + timeout=timeout, + ) + output = f"{proc.stdout}\n{proc.stderr}".lower() + has_help_text = "usage:" in output or "--help" in output + results.append( + { + "path": item["path"], + "command": f"python3 {item['path']} --help", + "returncode": proc.returncode, + "timed_out": False, + "passed": proc.returncode == 0 and has_help_text, + "has_help_text": has_help_text, + "stdout_excerpt": proc.stdout.strip()[:240], + "stderr_excerpt": proc.stderr.strip()[:240], + } + ) + except subprocess.TimeoutExpired as exc: + results.append( + { + "path": item["path"], + "command": f"python3 {item['path']} --help", + "returncode": None, + "timed_out": True, + "passed": False, + "has_help_text": False, + "stdout_excerpt": (exc.stdout or "").strip()[:240] if isinstance(exc.stdout, str) else "", + "stderr_excerpt": (exc.stderr or "").strip()[:240] if isinstance(exc.stderr, str) else "", + } + ) + failed = [item for item in results if not item["passed"]] + return { + "enabled": True, + "timeout_seconds": timeout, + "candidate_count": len(candidates), + "checked_count": len(results), + "passed_count": len(results) - len(failed), + "failed_count": len(failed), + "skipped_count": len(skipped), + "failed_scripts": [item["path"] for item in failed], + "results": results, + "skipped": skipped, + } + + +def disabled_help_smoke_status(scripts: list[dict[str, Any]], timeout: float) -> dict[str, Any]: + candidates = help_smoke_candidates(scripts) + return { + "enabled": False, + "timeout_seconds": timeout, + "candidate_count": len(candidates), + "checked_count": 0, + "passed_count": 0, + "failed_count": 0, + "skipped_count": len(scripts), + "failed_scripts": [], + "results": [], + "skipped": [{"path": item["path"], "reason": "help smoke disabled"} for item in scripts], + } + + +def build_trust_report(skill_dir: Path, run_help_smoke: bool = True, help_smoke_timeout: float = HELP_SMOKE_TIMEOUT_SECONDS) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + files = iter_scan_files(skill_dir) + secrets = scan_secrets(skill_dir, files) + scripts = script_inventory(skill_dir) + deps = dependency_status(skill_dir) + network_policy = network_policy_status(skill_dir, scripts) + help_smoke = ( + run_help_smoke_checks(skill_dir, scripts, help_smoke_timeout) + if run_help_smoke + else disabled_help_smoke_status(scripts, help_smoke_timeout) + ) + permission_governance = permission_governance_status(skill_dir, scripts) + trust = interface_trust(skill_dir) + failures = [] + warnings = [] + + if secrets: + failures.append(f"High-risk secret patterns found: {len(secrets)}") + if trust.get("remote_inline_execution") not in {"forbid", "deny", "false"}: + failures.append("remote_inline_execution must be forbid for governed release") + if deps["unpinned"]: + warnings.append(f"Unpinned dependency entries: {', '.join(deps['unpinned'])}") + if not deps["present"]: + warnings.append("No dependency or lock file detected") + internal_modules = [item for item in scripts if item["interface"] == INTERNAL_SCRIPT_INTERFACE] + missing_help = [ + item["path"] for item in scripts if item["interface"] != INTERNAL_SCRIPT_INTERFACE and not item["has_argparse"] + ] + if missing_help: + warnings.append(f"CLI scripts without argparse/help surface: {', '.join(missing_help[:8])}") + interactive = [item["path"] for item in scripts if item["uses_input"]] + if interactive: + warnings.append(f"Interactive scripts require reviewer awareness: {', '.join(interactive[:8])}") + network = [item["path"] for item in scripts if item["uses_network"]] + file_write = [item["path"] for item in scripts if item["uses_file_write"]] + if network_policy["missing_scripts"]: + warnings.append(f"Network-capable scripts require bounded host policy: {', '.join(network_policy['missing_scripts'][:8])}") + if network_policy["mismatches"]: + warning_paths = [item["path"] for item in network_policy["mismatches"]] + warnings.append(f"Network host policy mismatch: {', '.join(warning_paths[:8])}") + if help_smoke["failed_scripts"]: + warnings.append(f"CLI help smoke failed: {', '.join(help_smoke['failed_scripts'][:8])}") + if permission_governance["missing_capabilities"]: + warnings.append(f"Permission approvals missing: {', '.join(permission_governance['missing_capabilities'][:8])}") + if permission_governance["invalid_capabilities"]: + warnings.append(f"Permission approvals invalid: {', '.join(permission_governance['invalid_capabilities'][:8])}") + if permission_governance["expired_capabilities"]: + warnings.append(f"Permission approvals expired: {', '.join(permission_governance['expired_capabilities'][:8])}") + + summary = { + "scanned_files": len(files), + "script_count": len(scripts), + "internal_module_count": len(internal_modules), + "secret_findings": len(secrets), + "dependency_files": deps["present"], + "network_script_count": len(network), + "network_policy_covered_count": len(network_policy["covered_scripts"]), + "network_policy_missing_count": len(network_policy["missing_scripts"]), + "file_write_script_count": len(file_write), + "permission_required_count": permission_governance["required_count"], + "permission_approved_count": permission_governance["approval_count"], + "permission_missing_count": permission_governance["missing_count"], + "permission_invalid_count": permission_governance["invalid_count"], + "permission_expired_count": permission_governance["expired_count"], + "help_smoke_checked_count": help_smoke["checked_count"], + "help_smoke_failed_count": help_smoke["failed_count"], + "interactive_script_count": len(interactive), + "package_hash_scope": PACKAGE_HASH_SCOPE, + "package_hash_file_count": len(files), + "package_sha256": package_digest(files, skill_dir), + } + return { + "ok": not failures, + "skill_dir": display_path(skill_dir), + "summary": summary, + "failures": failures, + "warnings": warnings, + "secrets": secrets, + "scripts": scripts, + "dependencies": deps, + "network_policy": network_policy, + "help_smoke": help_smoke, + "permission_governance": permission_governance, + "trust_metadata": trust, + } + + +def render_markdown(payload: dict[str, Any]) -> str: + summary = payload["summary"] + lines = [ + "# Security Trust Report", + "", + f"- OK: `{payload['ok']}`", + f"- Scanned files: `{summary['scanned_files']}`", + f"- Scripts: `{summary['script_count']}`", + f"- Internal script modules: `{summary.get('internal_module_count', 0)}`", + f"- Secret findings: `{summary['secret_findings']}`", + f"- Network-capable scripts: `{summary['network_script_count']}`", + f"- Network policy covered scripts: `{summary.get('network_policy_covered_count', 0)}`", + f"- Network policy missing scripts: `{summary.get('network_policy_missing_count', 0)}`", + f"- File-write scripts: `{summary.get('file_write_script_count', 0)}`", + f"- Permission approvals: `{summary.get('permission_approved_count', 0)} / {summary.get('permission_required_count', 0)}`", + f"- Permission approval gaps: `{summary.get('permission_missing_count', 0) + summary.get('permission_invalid_count', 0) + summary.get('permission_expired_count', 0)}`", + f"- CLI help smoke checked: `{summary.get('help_smoke_checked_count', 0)}`", + f"- CLI help smoke failures: `{summary.get('help_smoke_failed_count', 0)}`", + f"- Interactive scripts: `{summary['interactive_script_count']}`", + f"- Package hash scope: `{summary['package_hash_scope']}`", + f"- Package hash files: `{summary['package_hash_file_count']}`", + f"- Package SHA256: `{summary['package_sha256']}`", + "", + "## Failures", + "", + ] + lines.extend([f"- {item}" for item in payload["failures"]] or ["- None"]) + lines.extend(["", "## Warnings", ""]) + lines.extend([f"- {item}" for item in payload["warnings"]] or ["- None"]) + lines.extend(["", "## Dependency Evidence", ""]) + lines.append(f"- Files: `{', '.join(payload['dependencies']['present']) or 'none'}`") + lines.append(f"- Pinned entries: `{len(payload['dependencies']['pinned'])}`") + lines.append(f"- Unpinned entries: `{len(payload['dependencies']['unpinned'])}`") + network_policy = payload.get("network_policy", {}) + lines.extend(["", "## Network Policy", ""]) + lines.append(f"- Policy file: `{network_policy.get('path', NETWORK_POLICY_REL_PATH)}`") + lines.append(f"- Present: `{network_policy.get('present', False)}`") + lines.append(f"- Covered scripts: `{len(network_policy.get('covered_scripts', []))}`") + lines.append(f"- Missing scripts: `{', '.join(network_policy.get('missing_scripts', [])) or 'none'}`") + lines.append(f"- Mismatches: `{len(network_policy.get('mismatches', []))}`") + permission_governance = payload.get("permission_governance", {}) + lines.extend(["", "## Permission Governance", ""]) + lines.append(f"- Policy file: `{permission_governance.get('path', PERMISSION_POLICY_REL_PATH)}`") + lines.append(f"- Present: `{permission_governance.get('present', False)}`") + lines.append(f"- Required capabilities: `{', '.join(permission_governance.get('required_capabilities', [])) or 'none'}`") + lines.append(f"- Approved capabilities: `{', '.join(permission_governance.get('approved_capabilities', [])) or 'none'}`") + lines.append(f"- Missing approvals: `{', '.join(permission_governance.get('missing_capabilities', [])) or 'none'}`") + lines.append(f"- Invalid approvals: `{', '.join(permission_governance.get('invalid_capabilities', [])) or 'none'}`") + lines.append(f"- Expired approvals: `{', '.join(permission_governance.get('expired_capabilities', [])) or 'none'}`") + help_smoke = payload.get("help_smoke", {}) + lines.extend(["", "## CLI Help Smoke", ""]) + lines.append(f"- Enabled: `{help_smoke.get('enabled', False)}`") + lines.append(f"- Timeout seconds: `{help_smoke.get('timeout_seconds', HELP_SMOKE_TIMEOUT_SECONDS)}`") + lines.append(f"- Checked scripts: `{help_smoke.get('checked_count', 0)}`") + lines.append(f"- Passed scripts: `{help_smoke.get('passed_count', 0)}`") + lines.append(f"- Failed scripts: `{', '.join(help_smoke.get('failed_scripts', [])) or 'none'}`") + lines.extend( + [ + "", + "## Script Surface", + "", + "| Script | Interface | Declared | Argparse | Main Guard | Input | Network | File Write | Subprocess | Reason |", + "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |", + ] + ) + for item in payload["scripts"]: + lines.append( + f"| {item['path']} | {item['interface']} | {item['interface_declared']} | {item['has_argparse']} | {item['has_main_guard']} | {item['uses_input']} | {item['uses_network']} | {item.get('uses_file_write', False)} | {item['uses_subprocess']} | {item['interface_reason']} |" + ) + return "\n".join(lines).strip() + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run Skill OS trust and security checks.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-json") + parser.add_argument("--output-md") + parser.add_argument("--skip-help-smoke", action="store_true") + parser.add_argument("--help-smoke-timeout", type=float, default=HELP_SMOKE_TIMEOUT_SECONDS) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir).resolve() + output_json = Path(args.output_json).resolve() if args.output_json else skill_dir / "reports" / "security_trust_report.json" + output_md = Path(args.output_md).resolve() if args.output_md else skill_dir / "reports" / "security_trust_report.md" + payload = build_trust_report( + skill_dir, + run_help_smoke=not args.skip_help_smoke, + help_smoke_timeout=args.help_smoke_timeout, + ) + payload["artifacts"] = {"json": display_path(output_json), "markdown": display_path(output_md)} + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(payload), encoding="utf-8") + print(json.dumps(payload, ensure_ascii=False, indent=2)) + raise SystemExit(0 if payload["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/trust_check_scripts.py b/scripts/trust_check_scripts.py new file mode 100644 index 0000000..44cdd7f --- /dev/null +++ b/scripts/trust_check_scripts.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +import ast +import re +from pathlib import Path +from typing import Any +from urllib.parse import urlparse + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Static script inventory helpers imported by trust_check.py." +INTERNAL_SCRIPT_INTERFACE = "internal-module" + + +def relpath(skill_dir: Path, path: Path) -> str: + return str(path.relative_to(skill_dir)) + + +def script_inventory(skill_dir: Path) -> list[dict[str, Any]]: + scripts_dir = skill_dir / "scripts" + if not scripts_dir.exists(): + return [] + inventory = [] + for path in sorted(scripts_dir.glob("*.py")): + text = path.read_text(encoding="utf-8", errors="replace") + flags = script_flags(text) + interface = script_interface(text) + urls = extract_url_literals(text) + inventory.append( + { + "path": relpath(skill_dir, path), + "interface": interface["name"], + "interface_declared": interface["declared"], + "interface_reason": interface["reason"], + "has_argparse": "argparse" in text, + "has_main_guard": 'if __name__ == "__main__"' in text, + "uses_input": flags["uses_input"], + "uses_network": flags["uses_network"], + "uses_file_write": flags["uses_file_write"], + "uses_subprocess": flags["uses_subprocess"], + "network_urls": urls, + "network_hosts": sorted({urlparse(url).hostname or "" for url in urls if urlparse(url).hostname}), + } + ) + return inventory + + +def string_assignment(tree: ast.Module, variable_name: str) -> str: + for node in tree.body: + value_node = None + if isinstance(node, ast.Assign): + if any(isinstance(target, ast.Name) and target.id == variable_name for target in node.targets): + value_node = node.value + elif isinstance(node, ast.AnnAssign): + target = node.target + if isinstance(target, ast.Name) and target.id == variable_name: + value_node = node.value + if isinstance(value_node, ast.Constant) and isinstance(value_node.value, str): + return value_node.value + return "" + + +def script_interface(text: str) -> dict[str, Any]: + try: + tree = ast.parse(text) + except SyntaxError: + match = re.search(r"SCRIPT_INTERFACE\s*=\s*['\"]([^'\"]+)['\"]", text) + reason_match = re.search(r"SCRIPT_INTERFACE_REASON\s*=\s*['\"]([^'\"]+)['\"]", text) + name = match.group(1) if match else "cli" + reason = reason_match.group(1) if reason_match else "" + return {"name": name, "declared": bool(match), "reason": reason} + + name = string_assignment(tree, "SCRIPT_INTERFACE") + reason = string_assignment(tree, "SCRIPT_INTERFACE_REASON") + if name: + return {"name": name, "declared": True, "reason": reason} + return {"name": "cli", "declared": False, "reason": "Default CLI classification; add SCRIPT_INTERFACE for internal modules."} + + +def extract_url_literals(text: str) -> list[str]: + values: list[str] = [] + try: + tree = ast.parse(text) + for node in ast.walk(tree): + if isinstance(node, ast.Constant) and isinstance(node.value, str): + values.append(node.value) + except SyntaxError: + values = re.findall(r"['\"]([^'\"]+)['\"]", text) + + urls = [] + seen = set() + for value in values: + for match in re.finditer(r"https?://[^\s'\"<>]+", value): + url = match.group(0).rstrip(").,]") + if url not in seen: + seen.add(url) + urls.append(url) + return urls + + +def call_name(node: ast.AST) -> str: + if isinstance(node, ast.Name): + return node.id + if isinstance(node, ast.Attribute): + base = call_name(node.value) + return f"{base}.{node.attr}" if base else node.attr + return "" + + +def script_flags(text: str) -> dict[str, bool]: + try: + tree = ast.parse(text) + except SyntaxError: + return { + "uses_input": bool(re.search(r"\b(?:input|getpass)\s*\(", text)), + "uses_network": bool(re.search(r"\b(?:urlopen|Request)\s*\(|\brequests\.", text)), + "uses_file_write": bool( + re.search(r"\.(?:write_text|write_bytes|mkdir|unlink)\s*\(", text) + or re.search(r"\b(?:open)\s*\([^)]*['\"][wa+x]", text) + or "shutil.rmtree" in text + ), + "uses_subprocess": "subprocess." in text, + } + call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)] + calls = [call_name(node.func) for node in call_nodes] + return { + "uses_input": any(name in {"input", "getpass.getpass"} or name.endswith(".getpass") for name in calls), + "uses_network": any(name in {"urlopen", "Request"} or name.startswith("requests.") for name in calls), + "uses_file_write": any(is_file_write_call(node, call_name(node.func)) for node in call_nodes), + "uses_subprocess": any(name.startswith("subprocess.") for name in calls), + } + + +def is_file_write_call(node: ast.Call, name: str) -> bool: + if name.endswith((".write_text", ".write_bytes", ".mkdir", ".unlink", ".rmdir")): + return True + if name in {"shutil.copy", "shutil.copy2", "shutil.copytree", "shutil.move", "shutil.rmtree"}: + return True + if name == "zipfile.ZipFile": + return True + if name in {"open", "Path.open"} or name.endswith(".open"): + args = list(node.args) + keywords = {keyword.arg: keyword.value for keyword in node.keywords if keyword.arg} + mode_node = args[1] if len(args) > 1 else keywords.get("mode") + if isinstance(mode_node, ast.Constant) and isinstance(mode_node.value, str): + return any(flag in mode_node.value for flag in ("w", "a", "x", "+")) + return False diff --git a/scripts/upgrade_check.py b/scripts/upgrade_check.py new file mode 100644 index 0000000..e801162 --- /dev/null +++ b/scripts/upgrade_check.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from datetime import date +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent +BUMP_RANK = {"none": 0, "patch": 1, "minor": 2, "major": 3} +COMPAT_RANK = {"missing": 0, "unknown": 0, "fail": 0, "warn": 1, "pass": 2} + + +def display_path(path: Path, root: Path = ROOT) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +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 package_from_payload(payload: dict[str, Any]) -> dict[str, Any]: + package = payload.get("package") + if isinstance(package, dict): + return package + return payload + + +def parse_version(value: str) -> tuple[int, int, int] | None: + match = re.match(r"^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$", str(value or "")) + if not match: + return None + return tuple(int(part) for part in match.groups()) + + +def declared_bump(previous: str, current: str) -> str: + old = parse_version(previous) + new = parse_version(current) + if old is None or new is None: + return "invalid" + if new < old: + return "downgrade" + if new == old: + return "none" + if new[0] > old[0]: + return "major" + if new[1] > old[1]: + return "minor" + return "patch" + + +def compare_sets(previous: list[Any], current: list[Any]) -> tuple[list[str], list[str]]: + old = {str(item) for item in previous} + new = {str(item) for item in current} + return sorted(new - old), sorted(old - new) + + +def compatibility_changes(previous: dict[str, Any], current: dict[str, Any]) -> list[dict[str, str]]: + targets = sorted(set(previous) | set(current)) + changes = [] + for target in targets: + old = str(previous.get(target, "missing")) + new = str(current.get(target, "missing")) + if old != new: + direction = "regressed" if COMPAT_RANK.get(new, 0) < COMPAT_RANK.get(old, 0) else "improved" + changes.append({"target": target, "from": old, "to": new, "direction": direction}) + return changes + + +def metadata_changes(previous: dict[str, Any], current: dict[str, Any]) -> list[dict[str, str]]: + fields = ["description", "maturity", "owner", "review_cadence", "trust_level", "license"] + changes = [] + for field in fields: + old = str(previous.get(field, "")) + new = str(current.get(field, "")) + if old != new: + changes.append({"field": field, "from": old, "to": new}) + return changes + + +def checksum_changes(previous: dict[str, Any], current: dict[str, Any]) -> list[dict[str, str]]: + old_checksums = previous.get("checksums", {}) if isinstance(previous.get("checksums"), dict) else {} + new_checksums = current.get("checksums", {}) if isinstance(current.get("checksums"), dict) else {} + changes = [] + for key in sorted(set(old_checksums) | set(new_checksums)): + old = str(old_checksums.get(key, "")) + new = str(new_checksums.get(key, "")) + if old != new: + changes.append({"field": key, "from": old, "to": new}) + return changes + + +def required_bump( + *, + name_changed: bool, + removed_targets: list[str], + added_targets: list[str], + compat: list[dict[str, str]], + metadata: list[dict[str, str]], + checksums: list[dict[str, str]], +) -> str: + if name_changed or removed_targets or any(item["direction"] == "regressed" for item in compat): + return "major" + if added_targets or any(item["field"] in {"description", "maturity", "trust_level"} for item in metadata): + return "minor" + if metadata or checksums or compat: + return "patch" + return "none" + + +def build_release_notes(diff: dict[str, Any], recommended: str, current: dict[str, Any]) -> list[str]: + notes = [f"Recommended version bump: {recommended}."] + if diff["removed_targets"]: + notes.append(f"Breaking target removals: {', '.join(diff['removed_targets'])}.") + if diff["added_targets"]: + notes.append(f"Added targets: {', '.join(diff['added_targets'])}.") + regressions = [item for item in diff["compatibility_changes"] if item["direction"] == "regressed"] + if regressions: + notes.append("Compatibility regressions: " + ", ".join(f"{item['target']} {item['from']}->{item['to']}" for item in regressions) + ".") + if diff["checksum_changes"]: + notes.append("Package or archive checksum changed; reviewers should verify package artifacts before release.") + if current.get("artifacts", {}).get("package_verification"): + notes.append(f"Package verification evidence: {current['artifacts']['package_verification']}.") + return notes + + +def run_upgrade_check(previous: dict[str, Any], current: dict[str, Any], generated_at: str) -> dict[str, Any]: + failures = [] + warnings = [] + previous_version = str(previous.get("version", "")) + current_version = str(current.get("version", "")) + bump = declared_bump(previous_version, current_version) + if bump == "invalid": + failures.append("Version comparison requires semver-like previous and current versions") + if bump == "downgrade": + failures.append("Current version is lower than previous version") + + added_targets, removed_targets = compare_sets(previous.get("targets", []), current.get("targets", [])) + compat = compatibility_changes(previous.get("compatibility", {}), current.get("compatibility", {})) + metadata = metadata_changes(previous, current) + checksums = checksum_changes(previous, current) + name_changed = str(previous.get("name", "")) != str(current.get("name", "")) + recommended = required_bump( + name_changed=name_changed, + removed_targets=removed_targets, + added_targets=added_targets, + compat=compat, + metadata=metadata, + checksums=checksums, + ) + if bump in BUMP_RANK and BUMP_RANK[bump] < BUMP_RANK[recommended]: + failures.append(f"Version bump is insufficient: declared {bump}, recommended {recommended}") + if name_changed: + failures.append("Package name changed; publish as a new package or use a major version bump") + if not checksums: + warnings.append("No checksum changes detected; confirm the previous package is the intended comparison baseline") + + diff = { + "name_changed": name_changed, + "added_targets": added_targets, + "removed_targets": removed_targets, + "compatibility_changes": compat, + "metadata_changes": metadata, + "checksum_changes": checksums, + } + return { + "ok": not failures, + "schema_version": "2.0", + "generated_at": generated_at, + "previous": { + "name": previous.get("name", ""), + "version": previous_version, + "targets": previous.get("targets", []), + }, + "current": { + "name": current.get("name", ""), + "version": current_version, + "targets": current.get("targets", []), + }, + "summary": { + "declared_bump": bump, + "recommended_bump": recommended, + "breaking_change_count": int(name_changed) + len(removed_targets) + sum(1 for item in compat if item["direction"] == "regressed"), + "added_target_count": len(added_targets), + "metadata_change_count": len(metadata), + "checksum_change_count": len(checksums), + "failure_count": len(failures), + "warning_count": len(warnings), + }, + "upgrade_diff": diff, + "release_notes": build_release_notes(diff, recommended, current), + "failures": failures, + "warnings": warnings, + } + + +def render_markdown(report: dict[str, Any]) -> str: + lines = [ + "# Upgrade Check", + "", + f"- OK: `{report['ok']}`", + f"- Previous: `{report['previous']['name']} {report['previous']['version']}`", + f"- Current: `{report['current']['name']} {report['current']['version']}`", + f"- Declared bump: `{report['summary']['declared_bump']}`", + f"- Recommended bump: `{report['summary']['recommended_bump']}`", + f"- Breaking changes: `{report['summary']['breaking_change_count']}`", + "", + "## Upgrade Diff", + "", + f"- Added targets: `{', '.join(report['upgrade_diff']['added_targets']) or 'none'}`", + f"- Removed targets: `{', '.join(report['upgrade_diff']['removed_targets']) or 'none'}`", + f"- Compatibility changes: `{len(report['upgrade_diff']['compatibility_changes'])}`", + f"- Metadata changes: `{len(report['upgrade_diff']['metadata_changes'])}`", + f"- Checksum changes: `{len(report['upgrade_diff']['checksum_changes'])}`", + "", + "## Release Notes", + "", + ] + lines.extend(f"- {item}" for item in report["release_notes"]) + lines.extend(["", "## Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) + lines.extend(["", "## Warnings", ""]) + lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"]) + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Compare current and previous registry package metadata for upgrade readiness.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--previous-package-json", required=True) + parser.add_argument("--current-package-json", default="reports/registry_audit.json") + parser.add_argument("--output-json", default="reports/upgrade_check.json") + parser.add_argument("--output-md", default="reports/upgrade_check.md") + parser.add_argument("--generated-at", default=str(date.today())) + args = parser.parse_args() + + previous = package_from_payload(load_json(Path(args.previous_package_json))) + current = package_from_payload(load_json(Path(args.current_package_json))) + report = run_upgrade_check(previous, current, args.generated_at) + report["artifacts"] = { + "previous_package": display_path(Path(args.previous_package_json)), + "current_package": display_path(Path(args.current_package_json)), + } + output_json = Path(args.output_json) + output_md = Path(args.output_md) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + report["artifacts"]["json"] = display_path(output_json) + report["artifacts"]["markdown"] = display_path(output_md) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/validate_skill.py b/scripts/validate_skill.py new file mode 100644 index 0000000..2b11a20 --- /dev/null +++ b/scripts/validate_skill.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +import argparse +import json +from pathlib import Path +import yaml + + +VALID_EXECUTION_CONTEXTS = {"inline", "fork"} +VALID_SHELLS = {"bash", "powershell"} +VALID_SOURCE_TIERS = {"local", "managed", "plugin", "remote"} + + +def main() -> None: + parser = argparse.ArgumentParser(description="Validate a skill package.") + parser.add_argument("skill_dir") + args = parser.parse_args() + + root = Path(args.skill_dir).resolve() + failures = [] + warnings = [] + skill_md = root / "SKILL.md" + interface = root / "agents" / "interface.yaml" + manifest = root / "manifest.json" + + if not skill_md.exists(): + failures.append("Missing SKILL.md") + if not interface.exists(): + failures.append("Missing agents/interface.yaml") + + if skill_md.exists(): + text = skill_md.read_text(encoding="utf-8") + if not text.startswith("---"): + failures.append("SKILL.md missing frontmatter") + else: + parts = text.split("---", 2) + data = yaml.safe_load(parts[1]) or {} + for field in ("name", "description"): + if not data.get(field): + failures.append(f"Missing frontmatter field: {field}") + + if interface.exists(): + data = yaml.safe_load(interface.read_text(encoding="utf-8")) or {} + meta = data.get("interface", {}) + compatibility = data.get("compatibility", {}) + activation = compatibility.get("activation", {}) + execution = compatibility.get("execution", {}) + trust = compatibility.get("trust", {}) + degradation = compatibility.get("degradation", {}) + for field in ("display_name", "short_description", "default_prompt"): + if not meta.get(field): + failures.append(f"Missing interface field: {field}") + if not compatibility.get("canonical_format"): + failures.append("Missing compatibility field: canonical_format") + adapter_targets = compatibility.get("adapter_targets") + if not adapter_targets or not isinstance(adapter_targets, list): + failures.append("Missing compatibility field: adapter_targets") + adapter_targets = [] + if not activation.get("mode"): + failures.append("Missing compatibility.activation.mode") + if activation.get("mode") == "path_scoped" and not activation.get("paths"): + failures.append("path_scoped activation requires compatibility.activation.paths") + if execution.get("context") not in VALID_EXECUTION_CONTEXTS: + failures.append("Invalid compatibility.execution.context") + if execution.get("shell") not in VALID_SHELLS: + failures.append("Invalid compatibility.execution.shell") + if trust.get("source_tier") not in VALID_SOURCE_TIERS: + failures.append("Invalid compatibility.trust.source_tier") + if trust.get("remote_inline_execution") not in {"forbid", "allow"}: + failures.append("Invalid compatibility.trust.remote_inline_execution") + if not trust.get("remote_metadata_policy"): + failures.append("Missing compatibility.trust.remote_metadata_policy") + for target in adapter_targets: + if target not in degradation: + failures.append(f"Missing compatibility.degradation entry for target: {target}") + + if manifest.exists(): + data = json.loads(manifest.read_text(encoding="utf-8")) + for field in ("name", "version", "owner", "updated_at"): + if not data.get(field): + failures.append(f"Missing manifest field: {field}") + if not data.get("review_cadence"): + warnings.append("Manifest exists without review_cadence.") + if not data.get("status"): + warnings.append("Manifest exists without status.") + if not data.get("maturity_tier"): + warnings.append("Manifest exists without maturity_tier.") + + print(json.dumps({"ok": not failures, "failures": failures, "warnings": warnings}, ensure_ascii=False, indent=2)) + if failures: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_package.py b/scripts/verify_package.py new file mode 100644 index 0000000..cc17eb7 --- /dev/null +++ b/scripts/verify_package.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import zipfile +from datetime import date +from pathlib import Path, PurePosixPath +from typing import Any + + +ROOT = Path(__file__).resolve().parent.parent + + +def display_path(path: Path, root: Path = ROOT) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +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 sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def zip_names(zip_path: Path) -> list[str]: + with zipfile.ZipFile(zip_path) as archive: + return archive.namelist() + + +def unsafe_zip_entries(names: list[str]) -> list[str]: + unsafe = [] + for name in names: + pure = PurePosixPath(name) + if pure.is_absolute() or ".." in pure.parts or name.startswith("\\") or (pure.parts and ":" in pure.parts[0]): + unsafe.append(name) + return unsafe + + +def generated_zip_entries(names: list[str]) -> list[str]: + generated = [] + for name in names: + parts = PurePosixPath(name).parts + if ".previews" in parts or "dist" in parts or (len(parts) > 2 and parts[1] == "tests" and any(part.startswith("tmp") for part in parts[2:])): + generated.append(name) + return generated + + +def non_root_skill_entries(names: list[str], package_root: str) -> list[str]: + root_entry = f"{package_root}/SKILL.md" + return sorted(name for name in names if PurePosixPath(name).name == "SKILL.md" and name != root_entry) + + +def required_targets(expectations: dict[str, Any], package_dir: Path) -> list[str]: + targets = expectations.get("required_targets") or [] + if targets: + return [str(item) for item in targets] + targets_dir = package_dir / "targets" + if not targets_dir.exists(): + return [] + return sorted(path.name for path in targets_dir.iterdir() if path.is_dir()) + + +def add_check(checks: list[dict[str, str]], failures: list[str], check_id: str, passed: bool, detail: str) -> None: + checks.append({"id": check_id, "status": "pass" if passed else "fail", "detail": detail}) + if not passed: + failures.append(detail) + + +def package_name(manifest: dict[str, Any], skill_dir: Path) -> str: + return str(manifest.get("name") or skill_dir.name) + + +def verify_package( + skill_dir: Path, + package_dir: Path, + expectations: dict[str, Any], + registry: dict[str, Any], + require_zip: bool, + generated_at: str, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + package_dir = package_dir.resolve() + checks: list[dict[str, str]] = [] + failures: list[str] = [] + warnings: list[str] = [] + + manifest_path = package_dir / "manifest.json" + manifest = load_json(manifest_path) + package_root = package_name(manifest, skill_dir) + add_check(checks, failures, "package-manifest", bool(manifest), f"Package manifest exists: {display_path(manifest_path)}") + + targets = required_targets(expectations, package_dir) + adapter_paths = [] + for target in targets: + adapter_path = package_dir / "targets" / target / "adapter.json" + adapter_paths.append(adapter_path) + adapter = load_json(adapter_path) + add_check(checks, failures, f"{target}-adapter", bool(adapter), f"Adapter exists for target: {target}") + for field in expectations.get("required_fields", []): + add_check( + checks, + failures, + f"{target}-field-{field}", + field in adapter, + f"{target} adapter includes field: {field}", + ) + required_files_by_target = { + key[: -len("_required_files")]: value + for key, value in expectations.items() + if key.endswith("_required_files") + } + for target, required_files in required_files_by_target.items(): + for rel in required_files: + add_check(checks, failures, f"{target}-file-{rel}", (package_dir / rel).exists(), f"Package contains {rel}") + + archive_path = package_dir / f"{package_root}.zip" + archive_sha = "" + archive_entries: list[str] = [] + if archive_path.exists(): + archive_sha = sha256_file(archive_path) + try: + archive_entries = zip_names(archive_path) + except zipfile.BadZipFile: + add_check(checks, failures, "archive-readable", False, f"Archive is not a readable zip: {display_path(archive_path)}") + else: + unsafe_entries = unsafe_zip_entries(archive_entries) + nested_skill_entries = non_root_skill_entries(archive_entries, package_root) + required_entries = [ + f"{package_root}/SKILL.md", + f"{package_root}/manifest.json", + f"{package_root}/agents/interface.yaml", + ] + add_check(checks, failures, "archive-safe-paths", not unsafe_entries, "Archive has no absolute or parent-traversal entries") + for entry in required_entries: + add_check(checks, failures, f"archive-entry-{entry}", entry in archive_entries, f"Archive contains {entry}") + add_check( + checks, + failures, + "archive-single-skill-entrypoint", + not nested_skill_entries, + "Archive exposes only the root SKILL.md entrypoint", + ) + generated_entries = generated_zip_entries(archive_entries) + add_check(checks, failures, "archive-excludes-generated", not generated_entries, "Archive excludes generated dist/, .previews/, and tests/tmp* contents") + elif require_zip: + add_check(checks, failures, "archive-present", False, f"Missing required package archive: {display_path(archive_path)}") + else: + warnings.append(f"Package archive not found: {display_path(archive_path)}") + + registry_package = registry.get("package", {}) if registry else {} + if registry_package: + add_check(checks, failures, "registry-ok", bool(registry.get("ok")), "Registry audit is OK") + add_check( + checks, + failures, + "registry-name-match", + registry_package.get("name") == manifest.get("name"), + "Registry package name matches package manifest", + ) + add_check( + checks, + failures, + "registry-version-match", + registry_package.get("version") == manifest.get("version"), + "Registry package version matches package manifest", + ) + compatibility = registry_package.get("compatibility", {}) + for target in targets: + add_check( + checks, + failures, + f"registry-compat-{target}", + compatibility.get(target) in {"pass", "warn"}, + f"Registry compatibility is reviewable for target: {target}", + ) + else: + warnings.append("Registry audit was not supplied; package verification skipped metadata parity checks.") + + report = { + "ok": not failures, + "schema_version": "2.0", + "generated_at": generated_at, + "skill_dir": display_path(skill_dir), + "package_dir": display_path(package_dir), + "summary": { + "target_count": len(targets), + "adapter_count": sum(1 for path in adapter_paths if path.exists()), + "archive_present": archive_path.exists(), + "archive_sha256": archive_sha, + "archive_entry_count": len(archive_entries), + "nested_skill_entry_count": len(non_root_skill_entries(archive_entries, package_root)), + "failure_count": len(failures), + "warning_count": len(warnings), + }, + "checks": checks, + "failures": failures, + "warnings": warnings, + "artifacts": { + "manifest": display_path(manifest_path), + "archive": display_path(archive_path) if archive_path.exists() else "", + }, + } + return report + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + lines = [ + "# Package Verification", + "", + f"- OK: `{report['ok']}`", + f"- Package directory: `{report['package_dir']}`", + f"- Targets: `{summary['adapter_count']} / {summary['target_count']}` adapters present", + f"- Archive present: `{summary['archive_present']}`", + f"- Archive SHA256: `{summary['archive_sha256'] or 'n/a'}`", + f"- Nested SKILL.md entries: `{summary.get('nested_skill_entry_count', 0)}`", + f"- Failures: `{summary['failure_count']}`", + f"- Warnings: `{summary['warning_count']}`", + "", + "## Checks", + "", + "| Check | Status | Detail |", + "| --- | --- | --- |", + ] + for item in report["checks"]: + lines.append(f"| `{item['id']}` | `{item['status']}` | {item['detail']} |") + lines.extend(["", "## Failures", ""]) + lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) + lines.extend(["", "## Warnings", ""]) + lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"]) + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description="Verify generated skill package artifacts and archive integrity.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--package-dir", default="dist") + parser.add_argument("--expectations", default="evals/packaging_expectations.json") + parser.add_argument("--registry-json", default="reports/registry_audit.json") + parser.add_argument("--output-json", default="reports/package_verification.json") + parser.add_argument("--output-md", default="reports/package_verification.md") + parser.add_argument("--require-zip", action="store_true") + parser.add_argument("--generated-at", default=str(date.today())) + args = parser.parse_args() + + skill_dir = Path(args.skill_dir) + package_dir = Path(args.package_dir) + if not package_dir.is_absolute(): + package_dir = Path.cwd() / package_dir + expectations = load_json(Path(args.expectations)) if args.expectations else {} + registry = load_json(Path(args.registry_json)) if args.registry_json else {} + report = verify_package(skill_dir, package_dir, expectations, registry, args.require_zip, args.generated_at) + + output_json = Path(args.output_json) + output_md = Path(args.output_md) + output_json.parent.mkdir(parents=True, exist_ok=True) + output_md.parent.mkdir(parents=True, exist_ok=True) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + output_md.write_text(render_markdown(report), encoding="utf-8") + report["artifacts"]["json"] = display_path(output_json) + report["artifacts"]["markdown"] = display_path(output_md) + output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps(report, ensure_ascii=False, indent=2)) + raise SystemExit(0 if report["ok"] else 2) + + +if __name__ == "__main__": + main() diff --git a/scripts/world_class_evidence_contract.py b/scripts/world_class_evidence_contract.py new file mode 100644 index 0000000..c15cae8 --- /dev/null +++ b/scripts/world_class_evidence_contract.py @@ -0,0 +1,553 @@ +#!/usr/bin/env python3 +import hashlib +import json +import re +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +from output_review_privacy import BLOCKED_DECISION_FIELDS +from world_class_human_evidence import validate_human_adjudication_report +from world_class_native_permission_evidence import validate_native_permission_report +from world_class_native_telemetry_evidence import validate_native_telemetry_report +from world_class_provider_evidence import validate_provider_execution_report + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by world-class evidence reports to share intake validation and artifact integrity checks." + +REQUIRED_TOP_LEVEL = [ + "schema_version", + "evidence_key", + "template_only", + "category", + "source_type", + "submitted_by", + "submitted_at", + "summary", + "artifact_refs", + "provenance", + "privacy", + "anti_overclaim", + "attestation", +] +REQUIRED_PRIVACY_FALSE = [ + "raw_user_content_included", + "raw_provider_prompt_included", + "credentials_included", + "secrets_included", +] +REQUIRED_ANTI_OVERCLAIM_FALSE = [ + "planned_work_counts_as_evidence", + "metadata_fallback_counts_as_native_enforcement", + "pending_review_counts_as_human_decision", + "local_command_runner_counts_as_provider_model", +] +REQUIRED_ATTESTATION_TRUE = [ + "real_external_or_human_evidence", + "reviewer_or_operator_identity_present", + "artifact_refs_reviewed", + "privacy_contract_satisfied", + "ledger_reviewer_approved", +] +EXPECTED_SOURCE_TYPES = { + "provider-holdout": "provider-output-eval", + "human-adjudication": "blind-ab-review", + "native-permission-enforcement": "runtime-permission-guard", + "native-client-telemetry": "native-client-telemetry", +} +SHA256_RE = re.compile(r"^[0-9a-fA-F]{64}$") +SUBMITTED_AT_RE = re.compile(r"^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}Z)?$") +DISALLOWED_REAL_ARTIFACTS = {"reports/telemetry_events.jsonl"} +REQUIRED_REAL_ARTIFACT_PATHS = { + "provider-holdout": {"reports/output_execution_runs.json"}, + "human-adjudication": { + "reports/output_review_adjudication.json", + "reports/output_review_decisions.json", + }, + "native-permission-enforcement": { + "reports/runtime_permission_probes.json", + "reports/install_simulation.json", + }, + "native-client-telemetry": { + "reports/adoption_drift_report.json", + "reports/telemetry_hook_recipes.json", + }, +} +EXPECTED_REAL_ARTIFACT_KINDS = { + "provider-holdout": { + "reports/output_execution_runs.json": "aggregate-report", + }, + "human-adjudication": { + "reports/output_review_adjudication.json": "adjudication-report", + "reports/output_review_decisions.json": "review-decisions", + }, + "native-permission-enforcement": { + "reports/runtime_permission_probes.json": "runtime-probe-report", + "reports/install_simulation.json": "installer-enforcement-report", + }, + "native-client-telemetry": { + "reports/adoption_drift_report.json": "adoption-drift-report", + "reports/telemetry_hook_recipes.json": "hook-recipes", + }, +} +PLACEHOLDER_FRAGMENTS = ( + "YYYY-MM-DD", + "name or team handle", + "operator with provider credentials", + "human reviewer", + "target client or installer integrator", + "Browser/Chrome/IDE/provider client integrator", + "Browser/Chrome/IDE/provider client", + "openai|claude|generic|vscode|other", + "client or installer component", + "/local/path/not/committed", +) +FORBIDDEN_REAL_SUBMISSION_FIELDS = BLOCKED_DECISION_FIELDS + + +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_json_with_status(path: Path) -> tuple[dict[str, Any], str]: + if not path.exists(): + return {}, "missing" + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return {}, "invalid-json" + if not isinstance(payload, dict): + return {}, "invalid-json" + return payload, "present" + + +def rel_path(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())) + except ValueError: + return str(path.resolve()) + + +def add_error(errors: list[str], condition: bool, message: str) -> None: + if not condition: + errors.append(message) + + +def has_placeholder_text(value: Any) -> bool: + text = str(value or "").strip() + if not text: + return False + lowered = text.lower() + return any(fragment.lower() in lowered for fragment in PLACEHOLDER_FRAGMENTS) + + +def require_real_text(errors: list[str], value: Any, field: str) -> None: + text = str(value or "").strip() + add_error(errors, bool(text), f"{field} is required") + add_error(errors, not has_placeholder_text(text), f"{field} must not use template placeholder text") + + +def parse_audit_timestamp(value: Any) -> datetime | None: + text = str(value or "").strip() + if not SUBMITTED_AT_RE.match(text): + return None + if "T" in text: + return datetime.fromisoformat(text.replace("Z", "+00:00")).astimezone(timezone.utc) + return datetime.fromisoformat(text).replace(tzinfo=timezone.utc) + + +def forbidden_submission_field_paths(value: Any, prefix: str = "$") -> list[str]: + found: list[str] = [] + if isinstance(value, dict): + for key, child in value.items(): + key_text = str(key) + child_path = f"{prefix}.{key_text}" + if key_text.strip().lower() in FORBIDDEN_REAL_SUBMISSION_FIELDS: + found.append(child_path) + found.extend(forbidden_submission_field_paths(child, child_path)) + elif isinstance(value, list): + for index, child in enumerate(value): + found.extend(forbidden_submission_field_paths(child, f"{prefix}[{index}]")) + return found + + +def validate_real_submission_file_identity( + payload: dict[str, Any], + errors: list[str], + *, + path: Path, + template_expected: bool, +) -> None: + if template_expected: + return + evidence_key = str(payload.get("evidence_key", "")).strip() + expected_name = f"{evidence_key}.json" + add_error( + errors, + path.name == expected_name, + f"real submission filename must be {expected_name}", + ) + + +def validate_real_submission_privacy_fields( + payload: dict[str, Any], + errors: list[str], + *, + template_expected: bool, +) -> None: + if template_expected: + return + blocked_paths = forbidden_submission_field_paths(payload) + add_error( + errors, + not blocked_paths, + "real submission must not include raw content, credential, secret, token, prompt, output, transcript, message, or answer-key fields: " + + ", ".join(blocked_paths[:8]), + ) + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def resolve_artifact_path(raw_path: str, root: Path) -> tuple[Path | None, str | None]: + text = raw_path.strip() + if any(token in text for token in ("<", ">", "*", "?")): + return None, "artifact_refs path must be concrete, not a placeholder or glob" + candidate = Path(text) + if candidate.is_absolute(): + return None, "artifact_refs path must be relative to the skill directory" + if ".." in candidate.parts: + return None, "artifact_refs path must not escape the skill directory" + resolved = (root / candidate).resolve() + try: + resolved.relative_to(root.resolve()) + except ValueError: + return None, "artifact_refs path must stay inside the skill directory" + return resolved, None + + +def validate_artifact_refs( + payload: dict[str, Any], + errors: list[str], + *, + root: Path, + template_expected: bool, +) -> dict[str, int]: + refs = payload.get("artifact_refs") + add_error(errors, isinstance(refs, list) and len(refs) > 0, "artifact_refs must contain at least one reference") + evidence_key = str(payload.get("evidence_key", "")) + required_paths = REQUIRED_REAL_ARTIFACT_PATHS.get(evidence_key, set()) + expected_kinds = EXPECTED_REAL_ARTIFACT_KINDS.get(evidence_key, {}) + observed_paths: set[str] = set() + seen_artifact_paths: set[str] = set() + stats = { + "artifact_ref_count": len(refs) if isinstance(refs, list) else 0, + "artifact_existing_count": 0, + "artifact_sha256_verified_count": 0, + "required_artifact_count": len(required_paths) if not template_expected else 0, + "required_artifact_verified_count": 0, + } + if not isinstance(refs, list): + return stats + for index, ref in enumerate(refs): + if not isinstance(ref, dict): + errors.append(f"artifact_refs[{index}] must be an object") + continue + path_text = str(ref.get("path", "")).strip() + add_error(errors, bool(path_text), f"artifact_refs[{index}].path is required") + add_error(errors, bool(str(ref.get("kind", "")).strip()), f"artifact_refs[{index}].kind is required") + add_error(errors, ref.get("contains_raw_content") is False, f"artifact_refs[{index}] must not contain raw content") + if template_expected or not path_text: + continue + candidate = Path(path_text) + if not candidate.is_absolute() and ".." not in candidate.parts and not any( + token in path_text for token in ("<", ">", "*", "?") + ): + observed_paths.add(candidate.as_posix()) + resolved, path_error = resolve_artifact_path(path_text, root) + if path_error: + errors.append(f"artifact_refs[{index}].path {path_error}") + continue + rel = rel_path(resolved, root) + add_error( + errors, + rel not in seen_artifact_paths, + f"artifact_refs[{index}].path must not duplicate another artifact reference", + ) + seen_artifact_paths.add(rel) + if rel in DISALLOWED_REAL_ARTIFACTS: + errors.append(f"artifact_refs[{index}].path must not reference raw local telemetry logs") + expected_kind = expected_kinds.get(rel) + if expected_kind: + observed_kind = str(ref.get("kind", "")).strip() + add_error( + errors, + observed_kind == expected_kind, + f"artifact_refs[{index}].kind must be {expected_kind} for {rel}", + ) + if not resolved.exists() or not resolved.is_file(): + errors.append(f"artifact_refs[{index}].path does not exist as a local file") + continue + stats["artifact_existing_count"] += 1 + declared = str(ref.get("sha256", "")).strip() + if not declared: + errors.append(f"artifact_refs[{index}].sha256 is required for a real submission") + continue + if not SHA256_RE.match(declared): + errors.append(f"artifact_refs[{index}].sha256 must be a 64-character hex digest") + continue + actual = sha256_file(resolved) + if actual.lower() != declared.lower(): + errors.append(f"artifact_refs[{index}].sha256 does not match local artifact") + continue + stats["artifact_sha256_verified_count"] += 1 + if rel in required_paths: + stats["required_artifact_verified_count"] += 1 + if not template_expected and required_paths: + missing_required = sorted(required_paths - observed_paths) + for path in missing_required: + errors.append(f"artifact_refs must include required evidence artifact {path}") + if not missing_required and stats["required_artifact_verified_count"] < len(required_paths): + errors.append("all required evidence artifacts must have verified sha256 digests") + return stats + + +def artifact_ref_path_map(payload: dict[str, Any], root: Path) -> dict[str, Path]: + refs = payload.get("artifact_refs", []) + if not isinstance(refs, list): + return {} + paths: dict[str, Path] = {} + for ref in refs: + if not isinstance(ref, dict): + continue + path_text = str(ref.get("path", "")).strip() + if not path_text: + continue + resolved, path_error = resolve_artifact_path(path_text, root) + if path_error or resolved is None: + continue + paths[rel_path(resolved, root)] = resolved + return paths + + +def validate_provider_holdout_artifacts(payload: dict[str, Any], errors: list[str], root: Path) -> None: + paths = artifact_ref_path_map(payload, root) + execution = load_json(paths.get("reports/output_execution_runs.json", root / "__missing__")) + if not execution: + return + provenance = payload.get("provenance", {}) if isinstance(payload.get("provenance", {}), dict) else {} + validate_provider_execution_report(execution, provenance, errors) + + +def validate_human_adjudication_artifacts(payload: dict[str, Any], errors: list[str], root: Path) -> None: + paths = artifact_ref_path_map(payload, root) + adjudication = load_json(paths.get("reports/output_review_adjudication.json", root / "__missing__")) + decisions = load_json(paths.get("reports/output_review_decisions.json", root / "__missing__")) + if not adjudication or not decisions: + return + provenance = payload.get("provenance", {}) if isinstance(payload.get("provenance", {}), dict) else {} + validate_human_adjudication_report(adjudication, decisions, provenance, errors) + + +def validate_native_permission_artifacts(payload: dict[str, Any], errors: list[str], root: Path) -> None: + paths = artifact_ref_path_map(payload, root) + probes = load_json(paths.get("reports/runtime_permission_probes.json", root / "__missing__")) + install = load_json(paths.get("reports/install_simulation.json", root / "__missing__")) + if probes: + validate_native_permission_report(probes, install, errors) + + +def validate_native_client_telemetry_artifacts(payload: dict[str, Any], errors: list[str], root: Path) -> None: + paths = artifact_ref_path_map(payload, root) + adoption = load_json(paths.get("reports/adoption_drift_report.json", root / "__missing__")) + recipes = load_json(paths.get("reports/telemetry_hook_recipes.json", root / "__missing__")) + validate_native_telemetry_report(adoption, recipes, errors) + + +def validate_real_artifact_payloads(payload: dict[str, Any], errors: list[str], root: Path, template_expected: bool) -> None: + if template_expected: + return + evidence_key = str(payload.get("evidence_key", "")) + if evidence_key == "provider-holdout": + validate_provider_holdout_artifacts(payload, errors, root) + elif evidence_key == "human-adjudication": + validate_human_adjudication_artifacts(payload, errors, root) + elif evidence_key == "native-permission-enforcement": + validate_native_permission_artifacts(payload, errors, root) + elif evidence_key == "native-client-telemetry": + validate_native_client_telemetry_artifacts(payload, errors, root) + + +def validate_evidence_specific(payload: dict[str, Any], errors: list[str]) -> None: + key = str(payload.get("evidence_key", "")) + template_expected = payload.get("template_only") is True + provenance = payload.get("provenance", {}) if isinstance(payload.get("provenance", {}), dict) else {} + if key == "provider-holdout": + add_error(errors, bool(str(provenance.get("provider", "")).strip()), "provider-holdout provenance.provider is required") + add_error(errors, bool(str(provenance.get("model", "")).strip()), "provider-holdout provenance.model is required") + add_error( + errors, + provenance.get("credential_material_committed") is False, + "provider-holdout must attest credential_material_committed is false", + ) + elif key == "human-adjudication": + add_error(errors, bool(str(provenance.get("reviewer", "")).strip()), "human-adjudication provenance.reviewer is required") + add_error(errors, bool(str(provenance.get("reviewed_at", "")).strip()), "human-adjudication provenance.reviewed_at is required") + if not template_expected: + require_real_text(errors, provenance.get("reviewer", ""), "human-adjudication provenance.reviewer") + add_error( + errors, + bool(SUBMITTED_AT_RE.match(str(provenance.get("reviewed_at", "")).strip())), + "human-adjudication provenance.reviewed_at must use YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ", + ) + add_error( + errors, + provenance.get("answer_key_opened_after_decisions") is True, + "human-adjudication must attest answer_key_opened_after_decisions is true", + ) + add_error( + errors, + provenance.get("blind_review_completed_before_answer_key") is True, + "human-adjudication must attest blind_review_completed_before_answer_key is true", + ) + add_error( + errors, + bool(str(provenance.get("blind_pack_sha256", "")).strip()), + "human-adjudication provenance.blind_pack_sha256 is required", + ) + add_error( + errors, + provenance.get("reviewer_reason_required") is True, + "human-adjudication must attest reviewer_reason_required is true", + ) + elif key == "native-permission-enforcement": + add_error(errors, bool(str(provenance.get("target", "")).strip()), "native-permission-enforcement provenance.target is required") + if not template_expected: + require_real_text(errors, provenance.get("target", ""), "native-permission-enforcement provenance.target") + add_error( + errors, + bool(str(provenance.get("guard_location", "")).strip()), + "native-permission-enforcement provenance.guard_location is required", + ) + if not template_expected: + require_real_text( + errors, + provenance.get("guard_location", ""), + "native-permission-enforcement provenance.guard_location", + ) + add_error( + errors, + str(provenance.get("guard_scope", "")).strip() + in {"target-client-native", "external-installer-runtime-guard"}, + "native-permission-enforcement provenance.guard_scope must be target-client-native or external-installer-runtime-guard", + ) + add_error( + errors, + provenance.get("guard_blocks_undeclared_capability") is True, + "native-permission-enforcement must attest guard_blocks_undeclared_capability is true", + ) + add_error( + errors, + provenance.get("metadata_fallback_retained_for_other_targets") is True, + "native-permission-enforcement must retain metadata fallback for non-native targets", + ) + elif key == "native-client-telemetry": + add_error(errors, bool(str(provenance.get("client", "")).strip()), "native-client-telemetry provenance.client is required") + if not template_expected: + require_real_text(errors, provenance.get("client", ""), "native-client-telemetry provenance.client") + if str(provenance.get("native_host_manifest", "")).strip(): + require_real_text( + errors, + provenance.get("native_host_manifest", ""), + "native-client-telemetry provenance.native_host_manifest", + ) + add_error(errors, provenance.get("event_source") == "external", "native-client-telemetry event_source must be external") + add_error(errors, provenance.get("metadata_only") is True, "native-client-telemetry must be metadata_only") + + +def validate_payload( + payload: dict[str, Any], + entry: dict[str, Any], + *, + path: Path, + root: Path, + template_expected: bool, +) -> dict[str, Any]: + errors: list[str] = [] + for key in REQUIRED_TOP_LEVEL: + add_error(errors, key in payload, f"missing {key}") + evidence_key = str(entry.get("key", "")) + add_error(errors, payload.get("schema_version") == "1.0", "schema_version must be 1.0") + add_error(errors, payload.get("evidence_key") == evidence_key, f"evidence_key must be {evidence_key}") + add_error(errors, payload.get("template_only") is template_expected, f"template_only must be {str(template_expected).lower()}") + add_error(errors, payload.get("category") == entry.get("category"), f"category must be {entry.get('category')}") + add_error( + errors, + payload.get("source_type") == EXPECTED_SOURCE_TYPES.get(evidence_key), + f"source_type must be {EXPECTED_SOURCE_TYPES.get(evidence_key)}", + ) + add_error(errors, bool(str(payload.get("submitted_by", "")).strip()), "submitted_by is required") + add_error(errors, bool(str(payload.get("submitted_at", "")).strip()), "submitted_at is required") + add_error(errors, bool(str(payload.get("summary", "")).strip()), "summary is required") + if not template_expected: + validate_real_submission_file_identity(payload, errors, path=path, template_expected=template_expected) + validate_real_submission_privacy_fields(payload, errors, template_expected=template_expected) + require_real_text(errors, payload.get("submitted_by", ""), "submitted_by") + add_error( + errors, + bool(SUBMITTED_AT_RE.match(str(payload.get("submitted_at", "")).strip())), + "submitted_at must use YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ", + ) + artifact_integrity = validate_artifact_refs(payload, errors, root=root, template_expected=template_expected) + privacy = payload.get("privacy", {}) if isinstance(payload.get("privacy", {}), dict) else {} + for key in REQUIRED_PRIVACY_FALSE: + add_error(errors, privacy.get(key) is False, f"privacy.{key} must be false") + anti_overclaim = payload.get("anti_overclaim", {}) if isinstance(payload.get("anti_overclaim", {}), dict) else {} + for key in REQUIRED_ANTI_OVERCLAIM_FALSE: + add_error(errors, anti_overclaim.get(key) is False, f"anti_overclaim.{key} must be false") + validate_evidence_specific(payload, errors) + attestation = payload.get("attestation", {}) if isinstance(payload.get("attestation", {}), dict) else {} + if not template_expected: + for key in REQUIRED_ATTESTATION_TRUE: + add_error(errors, attestation.get(key) is True, f"attestation.{key} must be true for a real submission") + ledger_reviewer = str(attestation.get("ledger_reviewer", "")).strip() + submitted_by = str(payload.get("submitted_by", "")).strip() + require_real_text(errors, ledger_reviewer, "attestation.ledger_reviewer") + add_error( + errors, + bool(SUBMITTED_AT_RE.match(str(attestation.get("ledger_reviewed_at", "")).strip())), + "attestation.ledger_reviewed_at must use YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ", + ) + add_error( + errors, + bool(ledger_reviewer and submitted_by and ledger_reviewer.casefold() != submitted_by.casefold()), + "attestation.ledger_reviewer must be different from submitted_by", + ) + submitted_at = parse_audit_timestamp(payload.get("submitted_at")) + ledger_reviewed_at = parse_audit_timestamp(attestation.get("ledger_reviewed_at")) + add_error( + errors, + bool(submitted_at and ledger_reviewed_at and ledger_reviewed_at >= submitted_at), + "attestation.ledger_reviewed_at must be at or after submitted_at", + ) + validate_real_artifact_payloads(payload, errors, root, template_expected) + return { + "path": rel_path(path, root), + "evidence_key": evidence_key, + "status": "pass" if not errors else "fail", + "template_only": template_expected, + "artifact_integrity": artifact_integrity, + "errors": errors, + } diff --git a/scripts/world_class_human_evidence.py b/scripts/world_class_human_evidence.py new file mode 100644 index 0000000..c0be8f8 --- /dev/null +++ b/scripts/world_class_human_evidence.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python3 +import re +from typing import Any + +from output_review_privacy import forbidden_decision_field_paths, forbidden_raw_content_field_paths + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by world_class_evidence_contract.py to validate blind A/B human adjudication evidence from decision rows." + +REVIEWED_AT_RE = re.compile(r"^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}Z)?$") + + +def add_error(errors: list[str], condition: bool, message: str) -> None: + if not condition: + errors.append(message) + + +def real_int(value: Any) -> int | None: + return value if isinstance(value, int) and not isinstance(value, bool) else None + + +def require_real_text(errors: list[str], value: Any, field: str) -> None: + add_error(errors, bool(str(value or "").strip()), f"{field} is required") + + +def object_list(value: Any) -> list[dict[str, Any]]: + return [item for item in value if isinstance(item, dict)] if isinstance(value, list) else [] + + +def case_ids(items: list[dict[str, Any]]) -> list[str]: + return [str(item.get("case_id", "")).strip() for item in items] + + +def duplicate_case_ids(ids: list[str]) -> list[str]: + seen: set[str] = set() + duplicates: list[str] = [] + for case_id in ids: + if case_id in seen and case_id not in duplicates: + duplicates.append(case_id) + seen.add(case_id) + return duplicates + + +def confidence_valid(value: Any) -> bool: + if value is None or value == "": + return True + if isinstance(value, bool): + return False + try: + parsed = float(value) + except (TypeError, ValueError): + return False + return 0 <= parsed <= 1 + + +def validate_adjudication_raw_content(adjudication: dict[str, Any], errors: list[str]) -> None: + blocked = forbidden_raw_content_field_paths(adjudication, "output_review_adjudication") + add_error( + errors, + not blocked, + "human-adjudication adjudication report must not include raw content, credential, secret, token, prompt, output, transcript, or message fields: " + + ", ".join(blocked[:8]), + ) + + +def validate_decision_rows( + decisions: list[dict[str, Any]], + expected_case_ids: set[str], + errors: list[str], +) -> None: + decision_ids = case_ids(decisions) + add_error(errors, not any(not case_id for case_id in decision_ids), "human-adjudication decisions must include case_id for every row") + duplicates = duplicate_case_ids([case_id for case_id in decision_ids if case_id]) + add_error(errors, not duplicates, "human-adjudication decisions must not contain duplicate case_id values") + add_error( + errors, + set(decision_ids) == expected_case_ids, + "human-adjudication decisions case_id set must match adjudication pairs", + ) + for index, item in enumerate(decisions, start=1): + blocked = forbidden_decision_field_paths(item, f"decisions[{index}]") + add_error( + errors, + not blocked, + "human-adjudication decisions must not include raw content or answer-key fields: " + + ", ".join(blocked[:8]), + ) + winner = str(item.get("winner_variant", "")).strip().upper() + add_error(errors, winner in {"A", "B"}, "human-adjudication decisions must include A/B winner_variant for every case") + add_error(errors, confidence_valid(item.get("confidence")), "human-adjudication decisions confidence must be between 0 and 1") + add_error(errors, bool(str(item.get("reason", "")).strip()), "human-adjudication decisions must include reviewer reason for every case") + + +def validate_review_integrity( + adjudication: dict[str, Any], + decisions: dict[str, Any], + expected_case_ids: set[str], + errors: list[str], +) -> None: + adjudication_integrity = adjudication.get("review_integrity", {}) + decisions_integrity = decisions.get("review_integrity", {}) + add_error( + errors, + isinstance(adjudication_integrity, dict) and bool(adjudication_integrity.get("blind_pack_sha256")), + "human-adjudication adjudication review_integrity.blind_pack_sha256 is required", + ) + add_error( + errors, + isinstance(decisions_integrity, dict) and bool(decisions_integrity.get("blind_pack_sha256")), + "human-adjudication decisions review_integrity.blind_pack_sha256 is required", + ) + if isinstance(adjudication_integrity, dict) and isinstance(decisions_integrity, dict): + add_error( + errors, + bool(adjudication_integrity.get("blind_pack_sha256")) + and adjudication_integrity.get("blind_pack_sha256") == decisions_integrity.get("blind_pack_sha256"), + "human-adjudication review_integrity.blind_pack_sha256 must match between decisions and adjudication", + ) + decision_case_ids = {str(item) for item in decisions_integrity.get("case_ids", []) if str(item).strip()} + adjudication_case_ids = {str(item) for item in adjudication_integrity.get("case_ids", []) if str(item).strip()} + add_error( + errors, + decision_case_ids == expected_case_ids and adjudication_case_ids == expected_case_ids, + "human-adjudication review_integrity.case_ids must match adjudicated case ids", + ) + + +def validate_reviewer_attestation( + adjudication: dict[str, Any], + decisions: dict[str, Any], + errors: list[str], +) -> None: + summary = adjudication.get("summary", {}) if isinstance(adjudication.get("summary", {}), dict) else {} + attestation = decisions.get("reviewer_attestation", {}) + add_error(errors, isinstance(attestation, dict), "human-adjudication decisions.reviewer_attestation must be an object") + if not isinstance(attestation, dict): + attestation = {} + for key in [ + "blind_review_completed_before_answer_key", + "answer_key_not_opened_before_decisions", + "raw_content_excluded", + "reviewer_reason_required", + ]: + add_error(errors, attestation.get(key) is True, f"human-adjudication decisions.reviewer_attestation.{key} must be true") + add_error( + errors, + summary.get("blind_review_attested") is True, + "human-adjudication adjudication summary.blind_review_attested must be true", + ) + add_error( + errors, + summary.get("raw_content_excluded_attested") is True, + "human-adjudication adjudication summary.raw_content_excluded_attested must be true", + ) + add_error( + errors, + summary.get("reviewer_reason_required_attested") is True, + "human-adjudication adjudication summary.reviewer_reason_required_attested must be true", + ) + + +def validate_adjudicated_pairs( + pairs: list[dict[str, Any]], + expected_count: int, + errors: list[str], +) -> set[str]: + pair_ids = case_ids(pairs) + add_error(errors, len(pairs) == expected_count, "human-adjudication adjudication pairs length must equal summary.pair_count") + add_error(errors, not any(not case_id for case_id in pair_ids), "human-adjudication adjudication pairs must include case_id") + duplicates = duplicate_case_ids([case_id for case_id in pair_ids if case_id]) + add_error(errors, not duplicates, "human-adjudication adjudication pairs must not contain duplicate case_id values") + for item in pairs: + case_id = str(item.get("case_id", "")).strip() + status = str(item.get("status", "")).strip() + add_error( + errors, + status in {"match", "disagree"}, + f"human-adjudication adjudication pair {case_id or ''} must be match or disagree", + ) + add_error( + errors, + item.get("expected_revealed") is True, + f"human-adjudication adjudication pair {case_id or ''} must reveal expected winner only after valid decision", + ) + add_error( + errors, + str(item.get("reviewer_winner_variant", "")).strip().upper() in {"A", "B"}, + f"human-adjudication adjudication pair {case_id or ''} must include reviewer_winner_variant", + ) + return {case_id for case_id in pair_ids if case_id} + + +def validate_human_adjudication_report( + adjudication: dict[str, Any], + decisions: dict[str, Any], + provenance: dict[str, Any], + errors: list[str], +) -> None: + summary = adjudication.get("summary", {}) if isinstance(adjudication.get("summary", {}), dict) else {} + pair_count = real_int(summary.get("pair_count")) + judgment_count = real_int(summary.get("judgment_count")) + pending_count = real_int(summary.get("pending_count")) + invalid_decision_count = real_int(summary.get("invalid_decision_count")) + answer_revealed_count = real_int(summary.get("answer_revealed_count")) + pending_answer_hidden_count = real_int(summary.get("pending_answer_hidden_count")) + checklist_ready_count = real_int(summary.get("reviewer_checklist_ready_count")) + checklist_count = real_int(summary.get("reviewer_checklist_count")) + + validate_adjudication_raw_content(adjudication, errors) + add_error(errors, adjudication.get("ok") is True, "human-adjudication adjudication report ok must be true") + add_error(errors, bool(pair_count and pair_count > 0), "human-adjudication adjudication summary.pair_count must be >0") + add_error( + errors, + bool(pair_count and judgment_count == pair_count), + "human-adjudication adjudication summary.judgment_count must equal summary.pair_count", + ) + add_error(errors, pending_count == 0, "human-adjudication adjudication summary.pending_count must be 0") + add_error(errors, invalid_decision_count == 0, "human-adjudication adjudication summary.invalid_decision_count must be 0") + add_error( + errors, + bool(pair_count and answer_revealed_count == pair_count), + "human-adjudication adjudication summary.answer_revealed_count must equal summary.pair_count", + ) + add_error(errors, pending_answer_hidden_count == 0, "human-adjudication adjudication summary.pending_answer_hidden_count must be 0") + add_error(errors, summary.get("needs_review") is False, "human-adjudication adjudication summary.needs_review must be false") + add_error( + errors, + summary.get("reviewer_metadata_present") is True, + "human-adjudication adjudication summary.reviewer_metadata_present must be true", + ) + add_error( + errors, + summary.get("reason_required") is True, + "human-adjudication adjudication summary.reason_required must be true", + ) + add_error( + errors, + summary.get("ready_for_human_evidence") is True, + "human-adjudication adjudication summary.ready_for_human_evidence must be true", + ) + if checklist_count is not None or checklist_ready_count is not None: + add_error( + errors, + bool(pair_count and checklist_count == pair_count and checklist_ready_count == pair_count), + "human-adjudication reviewer checklist must be ready for every pair", + ) + add_error(errors, not adjudication.get("failures"), "human-adjudication adjudication failures must be empty") + + decision_rows = object_list(decisions.get("decisions", [])) + add_error(errors, decisions.get("schema_version") == "1.0", "human-adjudication decisions.schema_version must be 1.0") + reviewer = str(decisions.get("reviewer", "")).strip() + reviewed_at = str(decisions.get("reviewed_at", "")).strip() + require_real_text(errors, reviewer, "human-adjudication decisions.reviewer") + add_error( + errors, + bool(REVIEWED_AT_RE.match(reviewed_at)), + "human-adjudication decisions.reviewed_at must use YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ", + ) + add_error( + errors, + bool(pair_count and len(decision_rows) == pair_count), + "human-adjudication decisions count must equal adjudication summary.pair_count", + ) + + expected_case_ids = validate_adjudicated_pairs(object_list(adjudication.get("pairs", [])), int(pair_count or 0), errors) + validate_decision_rows(decision_rows, expected_case_ids, errors) + validate_review_integrity(adjudication, decisions, expected_case_ids, errors) + validate_reviewer_attestation(adjudication, decisions, errors) + + provenance_reviewer = str(provenance.get("reviewer", "")).strip() + provenance_reviewed_at = str(provenance.get("reviewed_at", "")).strip() + add_error( + errors, + bool(reviewer and provenance_reviewer and reviewer == provenance_reviewer), + "human-adjudication provenance.reviewer must match decisions.reviewer", + ) + add_error( + errors, + bool(reviewed_at and provenance_reviewed_at and provenance_reviewed_at == reviewed_at), + "human-adjudication provenance.reviewed_at must match decisions.reviewed_at", + ) + add_error( + errors, + provenance.get("answer_key_opened_after_decisions") is True, + "human-adjudication provenance.answer_key_opened_after_decisions must be true", + ) + add_error( + errors, + provenance.get("blind_review_completed_before_answer_key") is True, + "human-adjudication provenance.blind_review_completed_before_answer_key must be true", + ) + add_error( + errors, + provenance.get("reviewer_reason_required") is True, + "human-adjudication provenance.reviewer_reason_required must be true", + ) + integrity = adjudication.get("review_integrity", {}) if isinstance(adjudication.get("review_integrity", {}), dict) else {} + add_error( + errors, + bool(provenance.get("blind_pack_sha256")) + and provenance.get("blind_pack_sha256") == integrity.get("blind_pack_sha256"), + "human-adjudication provenance.blind_pack_sha256 must match adjudication review_integrity.blind_pack_sha256", + ) + decision_fields = provenance.get("decision_fields", []) + required_decision_fields = {"case_id", "winner_variant", "reason"} + add_error( + errors, + isinstance(decision_fields, list) and required_decision_fields <= {str(item) for item in decision_fields}, + "human-adjudication provenance.decision_fields must include case_id, winner_variant, and reason", + ) + add_error(errors, adjudication.get("reviewer") == reviewer, "human-adjudication adjudication reviewer must match decisions.reviewer") + add_error( + errors, + adjudication.get("reviewed_at") == reviewed_at, + "human-adjudication adjudication reviewed_at must match decisions.reviewed_at", + ) diff --git a/scripts/world_class_native_permission_evidence.py b/scripts/world_class_native_permission_evidence.py new file mode 100644 index 0000000..f6ca6e3 --- /dev/null +++ b/scripts/world_class_native_permission_evidence.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +from typing import Any + +from output_review_privacy import forbidden_decision_field_paths + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by world_class_evidence_contract.py to validate runtime permission evidence from target-level probe rows." + + +def add_error(errors: list[str], condition: bool, message: str) -> None: + if not condition: + errors.append(message) + + +def real_int(value: Any) -> int | None: + return value if isinstance(value, int) and not isinstance(value, bool) else None + + +def object_list(value: Any) -> list[dict[str, Any]]: + return [item for item in value if isinstance(item, dict)] if isinstance(value, list) else [] + + +def sorted_strings(value: Any) -> list[str]: + return sorted(str(item) for item in value) if isinstance(value, list) else [] + + +def validate_no_raw_fields(payload: dict[str, Any], label: str, errors: list[str]) -> None: + blocked = forbidden_decision_field_paths(payload, label) + add_error( + errors, + not blocked, + f"native-permission-enforcement {label} must not include raw content, credential, secret, token, prompt, output, transcript, message, or answer-key fields: " + + ", ".join(blocked[:8]), + ) + + +def validate_target_rows(probes: dict[str, Any], summary: dict[str, Any], errors: list[str]) -> None: + targets = object_list(probes.get("targets", [])) + target_count = real_int(summary.get("target_count")) + native_count = real_int(summary.get("native_enforcement_count")) + fail_count = real_int(summary.get("fail_count")) + expected_capabilities = sorted_strings(probes.get("expected_capabilities")) + + add_error(errors, bool(targets), "native-permission-enforcement runtime probe targets must not be empty") + if target_count is not None: + add_error(errors, len(targets) == target_count, "native-permission-enforcement runtime probe targets length must equal summary.target_count") + add_error(errors, fail_count == 0, "native-permission-enforcement runtime probe summary.fail_count must be 0") + + observed_native = 0 + for index, target in enumerate(targets, start=1): + target_name = str(target.get("target", "")).strip() or str(index) + add_error(errors, target.get("status") == "pass", f"native-permission-enforcement target {target_name} status must be pass") + add_error(errors, not target.get("failures"), f"native-permission-enforcement target {target_name} failures must be empty") + checks = object_list(target.get("checks", [])) + add_error(errors, bool(checks), f"native-permission-enforcement target {target_name} checks must not be empty") + add_error( + errors, + all(item.get("passed") is True for item in checks), + f"native-permission-enforcement target {target_name} checks must all pass", + ) + native = target.get("native_enforcement") is True + if native: + observed_native += 1 + add_error( + errors, + target.get("assurance") == "native-enforced", + f"native-permission-enforcement target {target_name} assurance must be native-enforced", + ) + else: + add_error( + errors, + target.get("metadata_fallback_explicit") is True, + f"native-permission-enforcement fallback target {target_name} must keep metadata_fallback_explicit true", + ) + add_error( + errors, + bool(target.get("residual_risks")), + f"native-permission-enforcement fallback target {target_name} must retain residual risks", + ) + if expected_capabilities: + add_error( + errors, + sorted_strings(target.get("declared_capabilities")) == expected_capabilities, + f"native-permission-enforcement target {target_name} declared_capabilities must match expected_capabilities", + ) + installer = target.get("installer_enforcement", {}) if isinstance(target.get("installer_enforcement"), dict) else {} + add_error( + errors, + installer.get("enforced") is True, + f"native-permission-enforcement target {target_name} installer_enforcement.enforced must be true", + ) + add_error( + errors, + not installer.get("missing_capabilities"), + f"native-permission-enforcement target {target_name} installer_enforcement missing_capabilities must be empty", + ) + add_error( + errors, + not installer.get("failure_details"), + f"native-permission-enforcement target {target_name} installer_enforcement failure_details must be empty", + ) + + add_error(errors, bool(native_count and observed_native == native_count), "native-permission-enforcement native target rows must match summary.native_enforcement_count") + add_error(errors, observed_native > 0, "native-permission-enforcement must include at least one native-enforced target row") + + +def validate_install_simulation_report(install: dict[str, Any], errors: list[str]) -> None: + summary = install.get("summary", {}) if isinstance(install.get("summary", {}), dict) else {} + validate_no_raw_fields(install, "install_simulation", errors) + add_error(errors, install.get("ok") is True, "native-permission-enforcement install simulation report ok must be true") + add_error( + errors, + bool(real_int(summary.get("installer_permission_enforced_count")) and summary["installer_permission_enforced_count"] > 0), + "native-permission-enforcement install simulation summary.installer_permission_enforced_count must be >0", + ) + add_error( + errors, + summary.get("installer_permission_failure_count") == 0, + "native-permission-enforcement install simulation summary.installer_permission_failure_count must be 0", + ) + add_error(errors, summary.get("failure_count") == 0, "native-permission-enforcement install simulation summary.failure_count must be 0") + permission_checks = [ + item + for item in object_list(install.get("checks", [])) + if str(item.get("id", "")).startswith("permission-") and str(item.get("id", "")).endswith(("-approved", "-target-enforcement")) + ] + if permission_checks: + add_error( + errors, + all(item.get("status") == "pass" for item in permission_checks), + "native-permission-enforcement install simulation permission checks must all pass", + ) + + +def validate_native_permission_report(probes: dict[str, Any], install: dict[str, Any], errors: list[str]) -> None: + summary = probes.get("summary", {}) if isinstance(probes.get("summary", {}), dict) else {} + validate_no_raw_fields(probes, "runtime_permission_probes", errors) + add_error(errors, probes.get("ok") is True, "native-permission-enforcement runtime probe report ok must be true") + add_error( + errors, + bool(real_int(summary.get("native_enforcement_count")) and summary["native_enforcement_count"] > 0), + "native-permission-enforcement runtime probe summary.native_enforcement_count must be >0", + ) + add_error( + errors, + summary.get("failure_count") == 0, + "native-permission-enforcement runtime probe summary.failure_count must be 0", + ) + add_error( + errors, + summary.get("installer_enforcement_ready") is True, + "native-permission-enforcement runtime probe summary.installer_enforcement_ready must be true", + ) + validate_target_rows(probes, summary, errors) + if install: + validate_install_simulation_report(install, errors) diff --git a/scripts/world_class_native_telemetry_evidence.py b/scripts/world_class_native_telemetry_evidence.py new file mode 100644 index 0000000..6455ff6 --- /dev/null +++ b/scripts/world_class_native_telemetry_evidence.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +from typing import Any + +from output_review_privacy import forbidden_decision_field_paths + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by world_class_evidence_contract.py to validate native client telemetry evidence from metadata event rows." + +ADOPTION_EVENTS = {"skill_activation", "skill_output"} + + +def add_error(errors: list[str], condition: bool, message: str) -> None: + if not condition: + errors.append(message) + + +def real_int(value: Any) -> int | None: + return value if isinstance(value, int) and not isinstance(value, bool) else None + + +def object_list(value: Any) -> list[dict[str, Any]]: + return [item for item in value if isinstance(item, dict)] if isinstance(value, list) else [] + + +def validate_no_raw_fields(payload: dict[str, Any], label: str, errors: list[str]) -> None: + blocked = forbidden_decision_field_paths(payload, label) + add_error( + errors, + not blocked, + f"native-client-telemetry {label} must not include raw content, credential, secret, token, prompt, output, transcript, message, or answer-key fields: " + + ", ".join(blocked[:8]), + ) + + +def source_types(payload: dict[str, Any]) -> dict[str, Any]: + summary = payload.get("summary", {}) if isinstance(payload.get("summary"), dict) else {} + value = summary.get("source_types", {}) + return value if isinstance(value, dict) else {} + + +def validate_adoption_report(adoption: dict[str, Any], errors: list[str]) -> None: + summary = adoption.get("summary", {}) if isinstance(adoption.get("summary"), dict) else {} + privacy = adoption.get("privacy_contract", {}) if isinstance(adoption.get("privacy_contract"), dict) else {} + events = object_list(adoption.get("recent_events", [])) + adoption_rows = object_list(adoption.get("adoption_by_skill", [])) + external_summary_count = real_int(source_types(adoption).get("external")) + adoption_sample_count = real_int(summary.get("adoption_sample_count")) + + validate_no_raw_fields(adoption, "adoption_drift_report", errors) + add_error(errors, adoption.get("ok") is True, "native-client-telemetry adoption drift report ok must be true") + add_error( + errors, + bool(external_summary_count and external_summary_count > 0), + "native-client-telemetry adoption drift summary.source_types.external must be >0", + ) + add_error( + errors, + bool(adoption_sample_count and adoption_sample_count > 0), + "native-client-telemetry adoption drift summary.adoption_sample_count must be >0", + ) + add_error( + errors, + privacy.get("raw_content_allowed") is False, + "native-client-telemetry adoption drift privacy_contract.raw_content_allowed must be false", + ) + add_error( + errors, + privacy.get("raw_event_log_packaged") is False, + "native-client-telemetry adoption drift privacy_contract.raw_event_log_packaged must be false", + ) + add_error(errors, bool(events), "native-client-telemetry adoption drift recent_events must not be empty") + external_events = [item for item in events if item.get("source") == "external"] + external_adoption_events = [item for item in external_events if item.get("event") in ADOPTION_EVENTS] + add_error( + errors, + bool(external_summary_count and len(external_events) >= external_summary_count), + "native-client-telemetry external event rows must cover summary.source_types.external", + ) + add_error( + errors, + bool(external_adoption_events), + "native-client-telemetry must include at least one external adoption event row", + ) + add_error( + errors, + bool( + adoption_sample_count + and len([item for item in events if item.get("event") in ADOPTION_EVENTS]) >= adoption_sample_count + ), + "native-client-telemetry adoption event rows must cover summary.adoption_sample_count", + ) + add_error( + errors, + any((count := real_int(row.get("adoption_events"))) is not None and count > 0 for row in adoption_rows), + "native-client-telemetry adoption_by_skill must include at least one adoption event", + ) + + +def validate_recipe_report(recipes: dict[str, Any], errors: list[str]) -> None: + summary = recipes.get("summary", {}) if isinstance(recipes.get("summary"), dict) else {} + privacy = recipes.get("privacy_contract", {}) if isinstance(recipes.get("privacy_contract"), dict) else {} + recipe_rows = object_list(recipes.get("recipes", [])) + recipe_count = real_int(summary.get("recipe_count")) + metadata_count = real_int(summary.get("metadata_only_recipe_count")) + + validate_no_raw_fields(recipes, "telemetry_hook_recipes", errors) + add_error(errors, recipes.get("ok") is True, "native-client-telemetry hook recipes report ok must be true") + add_error( + errors, + bool(metadata_count and metadata_count > 0), + "native-client-telemetry hook recipes summary.metadata_only_recipe_count must be >0", + ) + add_error( + errors, + privacy.get("raw_content_allowed") is False, + "native-client-telemetry hook recipes privacy_contract.raw_content_allowed must be false", + ) + add_error(errors, bool(recipe_rows), "native-client-telemetry hook recipes rows must not be empty") + if recipe_count is not None: + add_error(errors, len(recipe_rows) == recipe_count, "native-client-telemetry hook recipes length must equal summary.recipe_count") + if metadata_count is not None: + add_error( + errors, + sum(1 for item in recipe_rows if item.get("metadata_only") is True) == metadata_count, + "native-client-telemetry metadata_only recipe rows must match summary.metadata_only_recipe_count", + ) + add_error( + errors, + all(item.get("source") == "external" for item in recipe_rows), + "native-client-telemetry hook recipe rows must use source external", + ) + add_error( + errors, + all(item.get("native_auto_capture") is False for item in recipe_rows), + "native-client-telemetry hook recipe rows must not claim native_auto_capture", + ) + + +def validate_native_telemetry_report(adoption: dict[str, Any], recipes: dict[str, Any], errors: list[str]) -> None: + if adoption: + validate_adoption_report(adoption, errors) + if recipes: + validate_recipe_report(recipes, errors) diff --git a/scripts/world_class_operator_runbook_coordination.py b/scripts/world_class_operator_runbook_coordination.py new file mode 100644 index 0000000..87d3eac --- /dev/null +++ b/scripts/world_class_operator_runbook_coordination.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +import json +from pathlib import Path +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Builds world-class operator coordination steps and release gates for render_world_class_operator_runbook.py." + + +def load_json(path: Path) -> dict[str, Any]: + try: + return json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return {} + + +COORDINATION_GUIDANCE: dict[str, dict[str, str]] = { + "provider-holdout": { + "phase": "provider-holdout", + "owner": "assistant + operator with provider credentials", + "user_action": "Provide the selected provider API key through an environment variable and confirm the provider, model, endpoint, and API format to use.", + "assistant_action": "Run provider-backed output execution, verify aggregate timing and token metadata, then prepare the evidence packet.", + "external_dependency": "Valid provider credentials and a live provider endpoint.", + "command": "python3 scripts/yao.py output-exec . --provider-runner --provider-model --timeout-seconds 60", + "pass_condition": "reports/output_execution_runs.json has model_executed_count > 0 and token_observed_count > 0.", + "artifact": "reports/output_execution_runs.json", + "privacy_boundary": "Commit aggregate metadata only; do not commit API keys, raw prompts, raw outputs, or provider request payloads.", + }, + "human-adjudication": { + "phase": "human-review", + "owner": "human reviewer + assistant", + "user_action": "Open the blind review kit, choose winners for all pairs, add reviewer metadata and reasons, and keep the answer key hidden until decisions are saved.", + "assistant_action": "Generate the review kit, import decisions, validate integrity, and prepare the human evidence packet.", + "external_dependency": "A real human reviewer who can make blind A/B judgments.", + "command": "python3 scripts/yao.py output-review-kit . && python3 scripts/yao.py output-review .", + "pass_condition": "reports/output_review_adjudication.json has pending_count == 0 and ready_for_human_evidence == true.", + "artifact": "reports/output_review_adjudication.json", + "privacy_boundary": "Store reviewer choices, reasons, hashes, and attestations; do not store raw prompts or answer-key exposure before decisions.", + }, + "native-permission-enforcement": { + "phase": "native-permission", + "owner": "target client or installer integrator + assistant", + "user_action": "Select a real target client or external installer guard that can enforce declared capabilities instead of metadata-only fallback.", + "assistant_action": "Run runtime permission probes, package verification, install simulation, and prepare the native enforcement evidence packet.", + "external_dependency": "A real target runtime, extension, client, or installer guard that can block undeclared capabilities.", + "command": "python3 scripts/yao.py runtime-permissions . --package-dir dist", + "pass_condition": "reports/runtime_permission_probes.json has native_enforcement_count > 0 and failure_count == 0.", + "artifact": "reports/runtime_permission_probes.json", + "privacy_boundary": "Do not mark metadata fallback as native enforcement; keep residual risks visible for fallback targets.", + }, + "native-client-telemetry": { + "phase": "native-telemetry", + "owner": "real client integrator + assistant", + "user_action": "Install the native host manifest in a real Browser, Chrome, IDE, or provider client and trigger a metadata-only event.", + "assistant_action": "Generate native host assets, import the external event JSONL, refresh adoption drift, and prepare the telemetry evidence packet.", + "external_dependency": "A real client process that emits metadata-only telemetry through the native host.", + "command": "python3 scripts/yao.py telemetry-import . --input-jsonl .yao/telemetry_spool/external_events.jsonl --source external", + "pass_condition": "reports/adoption_drift_report.json has source_types.external > 0 and adoption_sample_count > 0.", + "artifact": "reports/adoption_drift_report.json", + "privacy_boundary": "Telemetry must remain metadata-only; do not store raw prompts, outputs, transcripts, notes, or messages.", + }, +} + + +def build_coordination_plan(items: list[dict[str, Any]]) -> list[dict[str, Any]]: + pending_keys = [str(item.get("evidence_key", "")) for item in items if item.get("ledger_status") != "accepted"] + plan: list[dict[str, Any]] = [ + { + "step_id": "prepare-evidence-session", + "phase": "prepare", + "evidence_key": "", + "owner": "assistant + user", + "requires_user_input": True, + "user_action": "Confirm provider access, reviewer availability, target client path, and telemetry client path before collection starts.", + "assistant_action": "Run preflight and prepare submission drafts without accepting them as evidence.", + "external_dependency": "User-selected provider, reviewer, and real client surfaces.", + "command": "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts", + "pass_condition": "Preflight lists the same pending evidence keys and no credential values are printed.", + "artifact": "reports/world_class_evidence_preflight.json", + "privacy_boundary": "Drafts and preflight are planning artifacts only; they do not count as evidence.", + "counts_as_completion": False, + } + ] + for item in items: + key = str(item.get("evidence_key", "")) + if key not in pending_keys: + continue + guidance = COORDINATION_GUIDANCE.get(key, {}) + must_collect = item.get("must_collect", {}) if isinstance(item.get("must_collect", {}), dict) else {} + commands = item.get("commands", {}) if isinstance(item.get("commands", {}), dict) else {} + plan.append( + { + "step_id": f"collect-{key}", + "phase": guidance.get("phase", "collect-source"), + "evidence_key": key, + "owner": guidance.get("owner", item.get("owner", "release reviewer")), + "requires_user_input": True, + "user_action": guidance.get("user_action", item.get("blocking_reason", "")), + "assistant_action": guidance.get("assistant_action", "Validate source evidence and prepare the submission packet."), + "external_dependency": guidance.get("external_dependency", "Real external or human evidence source."), + "command": guidance.get("command", commands.get("prepare_submission", "")), + "pass_condition": guidance.get("pass_condition", "; ".join(must_collect.get("success_checks", []))), + "artifact": guidance.get("artifact", item.get("submission_path", "")), + "privacy_boundary": guidance.get("privacy_boundary", "; ".join(must_collect.get("privacy_contract", []))), + "counts_as_completion": False, + } + ) + plan.append( + { + "step_id": "review-and-release-gate", + "phase": "release-gate", + "evidence_key": "", + "owner": "assistant + ledger reviewer", + "requires_user_input": True, + "user_action": "Approve only validated evidence packets and confirm the release wording after the claim guard passes.", + "assistant_action": "Run intake, submission review, ledger, claim guard, benchmark, evidence consistency, Review Studio, and CI before final publish.", + "external_dependency": "Accepted evidence packets for every pending world-class key.", + "command": "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions && python3 scripts/yao.py world-class-claim-guard . && make ci-test", + "pass_condition": "Ledger ready_to_claim_world_class, benchmark public_claim_ready, claim guard violation_count == 0, Review Studio has no blockers, and CI passes.", + "artifact": "reports/world_class_evidence_ledger.json", + "privacy_boundary": "Release evidence may include aggregate reports and hashes only; raw content and credentials remain out of git.", + "counts_as_completion": False, + } + ) + return plan + + +def build_release_gate(skill_dir: Path, ledger: dict[str, Any]) -> dict[str, Any]: + reports_dir = skill_dir / "reports" + ledger_summary = ledger.get("summary", {}) if isinstance(ledger.get("summary", {}), dict) else {} + claim_guard = load_json(reports_dir / "world_class_claim_guard.json") + benchmark = load_json(reports_dir / "benchmark_reproducibility.json") + review_studio = load_json(reports_dir / "review-studio.json") + evidence_consistency = load_json(reports_dir / "evidence_consistency.json") + claim_summary = claim_guard.get("summary", {}) if isinstance(claim_guard.get("summary", {}), dict) else {} + benchmark_summary = benchmark.get("summary", {}) if isinstance(benchmark.get("summary", {}), dict) else {} + studio_summary = review_studio.get("summary", {}) if isinstance(review_studio.get("summary", {}), dict) else {} + consistency_summary = ( + evidence_consistency.get("summary", {}) + if isinstance(evidence_consistency.get("summary", {}), dict) + else {} + ) + checks = [ + { + "key": "world_class_ledger_ready", + "label": "World-class ledger ready", + "passed": ledger_summary.get("ready_to_claim_world_class") is True, + "current": ledger_summary.get("decision", "missing"), + "expected": "ready_to_claim_world_class == true", + "artifact": "reports/world_class_evidence_ledger.json", + }, + { + "key": "claim_guard_clean", + "label": "Claim guard clean", + "passed": claim_summary.get("violation_count") == 0 + and claim_summary.get("ledger_ready_to_claim_world_class") is True, + "current": ( + f"violations {claim_summary.get('violation_count', 'n/a')}; " + f"ledger ready {claim_summary.get('ledger_ready_to_claim_world_class', 'n/a')}" + ), + "expected": "violation_count == 0 and ledger_ready_to_claim_world_class == true", + "artifact": "reports/world_class_claim_guard.json", + }, + { + "key": "benchmark_public_claim_ready", + "label": "Benchmark public claim ready", + "passed": benchmark_summary.get("public_claim_ready") is True, + "current": f"public_claim_ready {benchmark_summary.get('public_claim_ready', 'n/a')}", + "expected": "public_claim_ready == true", + "artifact": "reports/benchmark_reproducibility.json", + }, + { + "key": "review_studio_clean", + "label": "Review Studio clean", + "passed": int(studio_summary.get("blocker_count", 1) or 0) == 0 + and int(studio_summary.get("warning_count", 1) or 0) == 0, + "current": ( + f"blockers {studio_summary.get('blocker_count', 'n/a')}; " + f"warnings {studio_summary.get('warning_count', 'n/a')}" + ), + "expected": "blocker_count == 0 and warning_count == 0", + "artifact": "reports/review-studio.json", + }, + { + "key": "evidence_consistency_clean", + "label": "Evidence consistency clean", + "passed": consistency_summary.get("decision") == "consistent" + and int(consistency_summary.get("fail_count", 1) or 0) == 0, + "current": consistency_summary.get("decision", "missing"), + "expected": "decision == consistent and fail_count == 0", + "artifact": "reports/evidence_consistency.json", + }, + ] + blocked = [item for item in checks if not item["passed"]] + return { + "ready": not blocked, + "decision": "ready-for-public-claim" if not blocked else "blocked-until-evidence-accepted", + "check_count": len(checks), + "blocked_count": len(blocked), + "checks": checks, + "blocked_checks": blocked, + "final_manual_check": "Run make ci-test in a clean worktree and verify GitHub Actions before converting the PR out of Draft.", + "counts_as_completion": False, + } diff --git a/scripts/world_class_phase_queue.py b/scripts/world_class_phase_queue.py new file mode 100644 index 0000000..ef88cc8 --- /dev/null +++ b/scripts/world_class_phase_queue.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Build phase-ordered operator queues for world-class evidence collection.""" + +from collections import defaultdict +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by prepare_world_class_submission_kit.py to group repair rows into an operator execution queue." + + +PHASE_LABELS = { + "select-evidence": "Select evidence", + "prepare-draft": "Prepare draft", + "unblock-access": "Unblock access", + "attach-artifacts": "Attach artifacts", + "collect-source": "Collect source", + "repair": "Repair", +} + + +def _phase_sort_key(row: dict[str, Any]) -> tuple[int, str, str]: + return ( + int(row.get("priority", 90) or 90), + str(row.get("evidence_key", "")), + str(row.get("target", "")), + ) + + +def build_phase_queue(repair_rows: list[dict[str, Any]]) -> list[dict[str, Any]]: + """Group repair rows by execution phase without changing evidence acceptance.""" + grouped: dict[str, list[dict[str, Any]]] = defaultdict(list) + for row in sorted(repair_rows, key=_phase_sort_key): + phase = str(row.get("phase", "repair") or "repair") + grouped[phase].append(row) + + queue: list[dict[str, Any]] = [] + for phase_rows in sorted(grouped.values(), key=lambda rows: _phase_sort_key(rows[0])): + first = phase_rows[0] + phase = str(first.get("phase", "repair") or "repair") + blocked_rows = [row for row in phase_rows if row.get("status") != "ready"] + owners = sorted({str(row.get("owner", "")).strip() for row in phase_rows if str(row.get("owner", "")).strip()}) + evidence_keys = sorted( + {str(row.get("evidence_key", "")).strip() for row in phase_rows if str(row.get("evidence_key", "")).strip()} + ) + queue.append( + { + "phase": phase, + "label": PHASE_LABELS.get(phase, phase.replace("-", " ").title()), + "priority": int(first.get("priority", 90) or 90), + "status": "blocked" if blocked_rows else "ready", + "blocked_count": len(blocked_rows), + "row_count": len(phase_rows), + "owners": owners, + "evidence_keys": evidence_keys, + "next_action_id": first.get("action_id", ""), + "next_action": first.get("next_action", ""), + "verification_command": first.get("verification_command", ""), + "counts_as_completion": False, + "rows": phase_rows, + } + ) + return queue + + +def summarize_phase_queue(queue: list[dict[str, Any]]) -> dict[str, Any]: + next_item = next((item for item in queue if item.get("status") != "ready"), queue[0] if queue else {}) + return { + "phase_queue_count": len(queue), + "phase_queue_blocked_count": sum(1 for item in queue if item.get("status") != "ready"), + "phase_queue_row_count": sum(int(item.get("row_count", 0) or 0) for item in queue), + "phase_queue_next_phase": next_item.get("phase", ""), + "phase_queue_next_action_id": next_item.get("next_action_id", ""), + "phase_queue_next_command": next_item.get("verification_command", ""), + "phase_queue_counts_as_completion": False, + } diff --git a/scripts/world_class_preflight_layout.py b/scripts/world_class_preflight_layout.py new file mode 100644 index 0000000..c17fbf4 --- /dev/null +++ b/scripts/world_class_preflight_layout.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python3 +"""HTML layout helpers for the world-class evidence preflight report.""" + +from typing import Any + +from html_rendering import html_text + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_world_class_preflight.py to keep preflight HTML layout out of data assembly." + + +def html_list(values: list[Any], empty: str) -> str: + if not values: + return f"
  • {html_text(empty)}
  • " + return "".join(f"
  • {html_text(value)}
  • " for value in values) + + +def render_html_commands(commands: dict[str, str]) -> str: + return "".join( + f"
  • {html_text(label.replace('_', ' '))}{html_text(command)}
  • " + for label, command in commands.items() + ) + + +def render_html_prechecks(rows: list[dict[str, Any]]) -> str: + if not rows: + return "

    No prechecks listed.

    " + return "".join( + """ +
    +
    + {kind} + {label} +
    +
    +
    Current
    {actual}
    +
    Status
    {status}
    +
    Action
    {action}
    +
    +
    + """.format( + status=html_text(row.get("status", "")), + kind=html_text(row.get("kind", "")), + label=html_text(row.get("label", "")), + actual=html_text(row.get("actual", "")), + action=html_text(row.get("next_action", "")), + ) + for row in rows + ) + + +def render_html_source_checks(rows: list[dict[str, Any]]) -> str: + if not rows: + return "

    No source checks listed.

    " + return "".join( + """ +
    +
    + {field} + {label} +
    +
    +
    Current
    {actual}
    +
    Expected
    {expected}
    +
    Status
    {status}
    +
    Action
    {action}
    +
    +
    + """.format( + status=html_text(row.get("status", "")), + field=html_text(row.get("field", "")), + label=html_text(row.get("label", "")), + actual=html_text(row.get("actual", "")), + expected=html_text(row.get("expected", "")), + action=html_text(row.get("next_action", "")), + ) + for row in rows + ) + + +def render_html_repair_rows(rows: list[dict[str, Any]]) -> str: + if not rows: + return "

    No repair rows listed.

    " + return "".join( + """ +
    +
    + {repair_type} + {target} +
    +
    +
    Priority
    {priority}
    +
    Phase
    {phase}
    +
    Owner
    {owner}
    +
    Status
    {status}
    +
    Reason
    {reason}
    +
    Action
    {action}
    +
    Verify
    {verify}
    +
    Evidence
    does not count as completion
    +
    +
    + """.format( + status=html_text(row.get("status", "")), + repair_type=html_text(row.get("repair_type", "")), + target=html_text(row.get("target", "")), + priority=html_text(row.get("priority", "")), + phase=html_text(row.get("phase", "")), + owner=html_text(row.get("owner", "")), + reason=html_text(row.get("blocking_reason", "")), + action=html_text(row.get("next_action", "")), + verify=html_text(row.get("verification_command", "")), + ) + for row in rows + ) + + +def render_html_phase_queue(rows: list[dict[str, Any]]) -> str: + if not rows: + return "

    No phase queue rows listed.

    " + return "".join( + """ +
    +
    + #{priority} · {status} +

    {label}

    +
    +
    +
    Phase
    {phase}
    +
    Rows
    {blocked} / {total} blocked
    +
    Owners
    {owners}
    +
    Evidence
    {evidence}
    +
    Next
    {action}
    +
    Verify
    {verify}
    +
    Counts
    does not count as completion
    +
    +
    + """.format( + status=html_text(row.get("status", "")), + priority=html_text(row.get("priority", "")), + label=html_text(row.get("label", "")), + phase=html_text(row.get("phase", "")), + blocked=html_text(row.get("blocked_count", 0)), + total=html_text(row.get("row_count", 0)), + owners=html_text(", ".join(str(owner) for owner in row.get("owners", [])) or "n/a"), + evidence=html_text(", ".join(str(key) for key in row.get("evidence_keys", [])) or "n/a"), + action=html_text(row.get("next_action", "")), + verify=html_text(row.get("verification_command", "")), + ) + for row in rows + ) + + +def render_html_artifact_roles(contract: dict[str, Any]) -> str: + cards = [] + for role in contract.get("roles", []): + role_name = str(role.get("role", "")) + if role_name == "submission-ref": + ready = f"{contract.get('submission_ref_ready_count', 0)}/{contract.get('submission_ref_total_count', 0)} ready" + else: + ready = ( + f"{contract.get('supporting_evidence_ready_count', 0)}/" + f"{contract.get('supporting_evidence_total_count', 0)} ready" + ) + cards.append( + """ +
    + {label} +

    {role}

    + {ready} +

    {description}

    + copy to artifact_refs: {copy} +
    + """.format( + label=html_text(role.get("label", "")), + role=html_text(role_name), + ready=html_text(ready), + description=html_text(role.get("description", "")), + copy=html_text(str(role.get("copy_to_artifact_refs") is True).lower()), + ) + ) + return "".join(cards) + + +def render_html_item(item: dict[str, Any]) -> str: + role_contract = item.get("submission_kit", {}).get("artifact_role_contract", {}) + return f""" +
    +
    + {html_text(item.get('category', ''))} · {html_text(item.get('status', ''))} +

    {html_text(item.get('label', item.get('evidence_key', '')))}

    +
    +
    +
    Evidence
    {html_text(item.get('evidence_key', ''))}
    +
    Ledger
    {html_text(item.get('ledger_status', ''))}
    +
    Intake
    {html_text(item.get('intake_readiness', ''))}
    +
    Review
    {html_text(item.get('review_state', ''))}
    +
    Draft
    {html_text(item.get('submission_path', ''))}
    +
    +
    +

    Next Action

    +

    {html_text(item.get('next_action', ''))}

    + {html_text(item.get('commands', {}).get('prepare_submission', ''))} + {html_text(item.get('commands', {}).get('prepare_prefilled_submission', ''))} +
    +
    +

    Artifact Roles

    +
    {render_html_artifact_roles(role_contract)}
    +
    +
    +

    Prechecks

    +
    {render_html_prechecks(item.get('prechecks', []))}
    +
    +
    +

    Source Checks

    +
    {render_html_source_checks(item.get('source_checklist', []))}
    +
    +
    +

    Repair Rows

    +
    {render_html_repair_rows(item.get('repair_checklist', []))}
    +
    +
    +

    Runbook

    +
      {html_list(item.get('runbook', []), 'No runbook steps listed.')}
    +
    +
    + """ + + +def render_html(report: dict[str, Any]) -> str: + summary = report["summary"] + stats = [ + ("Decision", summary["decision"]), + ("Pending", summary["pending_count"]), + ("Ready", summary["collection_ready_count"]), + ("Blocked", summary["collection_blocked_count"]), + ("Source", f"{summary['source_pass_count']}/{summary['source_check_count']}"), + ("Repairs", f"{summary.get('repair_blocked_count', 0)}/{summary.get('repair_checklist_count', 0)}"), + ("Phases", f"{summary.get('phase_queue_blocked_count', 0)}/{summary.get('phase_queue_count', 0)}"), + ] + stat_html = "".join( + f"
    {html_text(label)}{html_text(value)}
    " + for label, value in stats + ) + role_contract = report["submissions"]["artifact_role_contract"] + item_cards = "".join(render_html_item(item) for item in report.get("items", [])) + phase_queue_html = render_html_phase_queue(report.get("phase_queue", [])) + html = f""" + + + + + World-Class Evidence Preflight + + + + +
    +
    + Evidence Collection +

    World-Class Evidence Preflight

    +

    This operator view shows which external and human evidence is still blocked, which commands prepare editable submission drafts, and why preflight never counts as accepted evidence.

    +
    {stat_html}
    +
    +
    +
    +

    Submission Kit

    +

    Generate drafts only after real provider, human-review, native-permission, or native-client work exists. Drafts remain non-evidence until valid aggregate artifact refs and SHA-256 digests are supplied. Artifact prefill is convenience data only.

    +
      +
    • submissions directory: {html_text(report['submissions']['directory'])}
    • +
    • drafts count as evidence: {html_text(str(report['submissions']['drafts_count_as_evidence']).lower())}
    • +
    • artifact prefill counts as evidence: {html_text(str(report['submissions']['artifact_prefill_counts_as_evidence']).lower())}
    • +
    • preflight accepts evidence: {html_text(str(report['summary']['preflight_counts_as_evidence']).lower())}
    • +
    +
    {render_html_artifact_roles(role_contract)}
    +

    submission-ref rows are the paths expected in artifact_refs; supporting-evidence rows stay available for audit context.

    +
    + +
    +
    +

    Phase Queue

    +

    Repair rows are grouped into execution phases so operators can clear the first blocker before moving to later source collection. Phase queue rows remain procedural and never count as accepted evidence.

    +
    {phase_queue_html}
    +

    Evidence Queue

    +
    {item_cards}
    +
    +
    +

    Safety Boundary

    +
    • Environment variables are displayed only as set or not-set; secret values are never printed.
    • Human-required and external-required states are operator work, not accepted evidence.
    • The world-class ledger remains the only source of truth for ready_to_claim_world_class.
    +
    +
    + + +""" + return "\n".join(line.rstrip() for line in html.splitlines()) + "\n" diff --git a/scripts/world_class_preflight_markdown.py b/scripts/world_class_preflight_markdown.py new file mode 100644 index 0000000..da074e0 --- /dev/null +++ b/scripts/world_class_preflight_markdown.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +"""Markdown renderer for the world-class evidence preflight report.""" + +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_world_class_preflight.py to keep Markdown layout separate from preflight data assembly." + + +def md_cell(value: Any) -> str: + return str(value).replace("|", "\\|") + + +def render_markdown(report: dict[str, Any]) -> str: + summary = report["summary"] + role_contract = report["submissions"]["artifact_role_contract"] + lines = [ + "# World-Class Evidence Preflight", + "", + f"Generated at: `{report['generated_at']}`", + "", + "## Summary", + "", + f"- decision: `{summary['decision']}`", + f"- ready to claim world-class: `{str(summary['ready_to_claim_world_class']).lower()}`", + f"- preflight counts as evidence: `{str(summary['preflight_counts_as_evidence']).lower()}`", + f"- credential value exposed: `{str(summary['credential_value_exposed']).lower()}`", + f"- collection ready: `{summary['collection_ready_count']}`", + f"- collection blocked: `{summary['collection_blocked_count']}`", + f"- source checks: `{summary['source_pass_count']}` pass / `{summary['source_check_count']}` total", + f"- repair rows: `{summary['repair_blocked_count']}` blocked / `{summary['repair_checklist_count']}` total", + f"- phase queue: `{summary['phase_queue_blocked_count']}` blocked / `{summary['phase_queue_count']}` phases", + f"- phase queue rows: `{summary['phase_queue_row_count']}`", + f"- next repair action: `{summary.get('next_repair_action_id', '')}`", + f"- next repair owner: `{summary.get('next_repair_owner', '')}`", + f"- next phase: `{summary.get('phase_queue_next_phase', '')}`", + f"- next phase action: `{summary.get('phase_queue_next_action_id', '')}`", + "", + "This preflight report checks whether an operator can start collecting the remaining external or human evidence. It never accepts evidence, prints secret values, or changes the world-class ledger.", + "", + "## Submission Kit Handoff", + "", + f"- submissions directory: `{report['submissions']['directory']}`", + f"- prepare drafts: `{report['submissions']['commands']['prepare_submission']}`", + f"- prepare drafts with artifact SHA prefill: `{report['submissions']['commands']['prepare_prefilled_submission']}`", + f"- validate intake: `{report['submissions']['commands']['validate_intake']}`", + f"- review queue: `{report['submissions']['commands']['submission_review']}`", + f"- refresh ledger: `{report['submissions']['commands']['refresh_ledger']}`", + f"- guard claims: `{report['submissions']['commands']['guard_claim']}`", + f"- drafts count as evidence: `{str(report['submissions']['drafts_count_as_evidence']).lower()}`", + f"- artifact prefill counts as evidence: `{str(report['submissions']['artifact_prefill_counts_as_evidence']).lower()}`", + f"- submission refs ready: `{role_contract['submission_ref_ready_count']}` / `{role_contract['submission_ref_total_count']}`", + f"- supporting evidence ready: `{role_contract['supporting_evidence_ready_count']}` / `{role_contract['supporting_evidence_total_count']}`", + "", + "Generate the submission kit after the real provider, human, native-permission, or native-client work exists. The generated JSON drafts remain `template_only: true` until an operator edits them with real aggregate artifact references and matching SHA-256 digests. The prefill command only inserts local artifact SHA-256 digests; it does not make a draft count as evidence.", + "", + "| Role | Copy to artifact_refs | Ready | Meaning |", + "| --- | --- | --- | --- |", + ] + for role in role_contract.get("roles", []): + if role.get("role") == "submission-ref": + ready = f"{role_contract['submission_ref_ready_count']} / {role_contract['submission_ref_total_count']}" + else: + ready = ( + f"{role_contract['supporting_evidence_ready_count']} / " + f"{role_contract['supporting_evidence_total_count']}" + ) + lines.append( + f"| `{role['role']}` | `{str(role['copy_to_artifact_refs']).lower()}` | `{ready}` | " + f"{md_cell(role['description'])} |" + ) + lines.extend( + [ + "", + "`submission-ref` rows are the only checklist rows expected in `artifact_refs`; `supporting-evidence` rows stay available for audit context and reviewer traceability.", + "", + "## Phase Queue", + "", + "Phase queue rows group the same repair checklist into operator execution phases. They are procedural guidance only and do not count as completion evidence.", + "", + "| Priority | Phase | Status | Rows | Owners | Evidence | Verify | Next action |", + "| --- | --- | --- | --- | --- | --- | --- | --- |", + ] + ) + for row in report.get("phase_queue", []): + owners = ", ".join(str(owner) for owner in row.get("owners", [])) + evidence_keys = ", ".join(str(key) for key in row.get("evidence_keys", [])) + rows = f"{row.get('blocked_count', 0)} / {row.get('row_count', 0)} blocked" + lines.append( + f"| `{row.get('priority', '')}` | `{row.get('phase', '')}` | `{row.get('status', '')}` | " + f"{md_cell(rows)} | {md_cell(owners)} | {md_cell(evidence_keys)} | " + f"`{md_cell(row.get('verification_command', ''))}` | {md_cell(row.get('next_action', ''))} |" + ) + lines.extend( + [ + "", + "## Evidence Items", + "", + "| Evidence | Status | Intake | Review | Next action |", + "| --- | --- | --- | --- | --- |", + ] + ) + for item in report["items"]: + next_action = str(item.get("next_action", "")).replace("|", "\\|") + lines.append( + f"| `{item['evidence_key']}` | `{item['status']}` | `{item['intake_readiness']}` | `{item['review_state']}` | {next_action} |" + ) + lines.extend( + [ + "", + "## Repair Checklist", + "", + "Repair rows convert preflight and source blockers into a prioritized operator queue. They are guidance only and do not count as completion evidence.", + "", + "| Priority | Phase | Owner | Evidence | Type | Target | Status | Verify | Next action |", + "| --- | --- | --- | --- | --- | --- | --- | --- | --- |", + ] + ) + for row in report.get("repair_checklist", []): + lines.append( + f"| `{row.get('priority', '')}` | `{row.get('phase', '')}` | {md_cell(row.get('owner', ''))} | " + f"`{row['evidence_key']}` | `{row['repair_type']}` | `{row['target']}` | " + f"`{row['status']}` | `{md_cell(row.get('verification_command', ''))}` | {md_cell(row['next_action'])} |" + ) + for item in report["items"]: + lines.extend( + [ + "", + f"## {item['label']}", + "", + f"- status: `{item['status']}`", + f"- ledger: `{item['ledger_status']}`", + f"- submission: `{item.get('submission_path') or 'missing'}`", + f"- prepare draft: `{item['commands']['prepare_submission']}`", + f"- prepare draft with artifact SHA prefill: `{item['commands']['prepare_prefilled_submission']}`", + f"- submission refs ready: `{item['submission_kit']['artifact_role_contract']['submission_ref_ready_count']}` / `{item['submission_kit']['artifact_role_contract']['submission_ref_total_count']}`", + f"- supporting evidence ready: `{item['submission_kit']['artifact_role_contract']['supporting_evidence_ready_count']}` / `{item['submission_kit']['artifact_role_contract']['supporting_evidence_total_count']}`", + "", + "### Prechecks", + "", + "| Check | Kind | Current | Status | Next action |", + "| --- | --- | --- | --- | --- |", + ] + ) + for row in item.get("prechecks", []): + action = md_cell(row.get("next_action", "")) + lines.append( + f"| {row['label']} | `{row['kind']}` | `{row['actual']}` | `{row['status']}` | {action} |" + ) + lines.extend( + [ + "", + "### Source Checks", + "", + "| Check | Current | Expected | Status | Next action |", + "| --- | --- | --- | --- | --- |", + ] + ) + for row in item.get("source_checklist", []): + action = md_cell(row.get("next_action", "")) + lines.append( + f"| {row['label']} | `{row['actual']}` | `{row['expected']}` | `{row['status']}` | {action} |" + ) + lines.extend( + [ + "", + "## Boundary", + "", + "- Environment variables are reported only as `set` or `not-set`; values are never printed.", + "- Human-required and external-required states are operator actions, not accepted evidence.", + "- The world-class ledger remains the source of truth for `ready_to_claim_world_class`.", + ] + ) + return "\n".join(lines).rstrip() + "\n" diff --git a/scripts/world_class_provider_evidence.py b/scripts/world_class_provider_evidence.py new file mode 100644 index 0000000..271e35e --- /dev/null +++ b/scripts/world_class_provider_evidence.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +import re +from typing import Any + +from output_review_privacy import forbidden_decision_field_paths + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by world_class_evidence_contract.py to validate provider-backed holdout execution evidence from run rows." + +SHA256_RE = re.compile(r"^[0-9a-fA-F]{64}$") + + +def add_error(errors: list[str], condition: bool, message: str) -> None: + if not condition: + errors.append(message) + + +def real_int(value: Any) -> int | None: + return value if isinstance(value, int) and not isinstance(value, bool) else None + + +def summary(payload: dict[str, Any]) -> dict[str, Any]: + value = payload.get("summary", {}) + return value if isinstance(value, dict) else {} + + +def normalized(value: Any) -> str: + return str(value or "").strip().casefold() + + +def run_rows(execution: dict[str, Any]) -> list[dict[str, Any]]: + runs = execution.get("runs", []) + return [item for item in runs if isinstance(item, dict)] if isinstance(runs, list) else [] + + +def validate_no_raw_fields(execution: dict[str, Any], errors: list[str]) -> None: + blocked = forbidden_decision_field_paths(execution, "output_execution_runs") + add_error( + errors, + not blocked, + "provider-holdout output execution report must not include raw content, credential, secret, token, prompt, output, transcript, message, or answer-key fields: " + + ", ".join(blocked[:8]), + ) + + +def positive_duration(value: Any) -> bool: + return isinstance(value, (int, float)) and not isinstance(value, bool) and value > 0 + + +def observed_usage(run: dict[str, Any]) -> bool: + usage = run.get("usage", {}) + if not isinstance(usage, dict) or usage.get("estimated") is not False: + return False + token_values = [ + real_int(usage.get("input_tokens")), + real_int(usage.get("output_tokens")), + real_int(usage.get("total_tokens")), + ] + return all(value is not None and value > 0 for value in token_values) + + +def provider_model_runs(execution: dict[str, Any], provenance: dict[str, Any]) -> list[dict[str, Any]]: + expected_provider = normalized(provenance.get("provider")) + expected_model = normalized(provenance.get("model")) + matches: list[dict[str, Any]] = [] + for run in run_rows(execution): + if run.get("status") != "pass": + continue + if run.get("model_executed") is not True or run.get("execution_mode") != "model": + continue + if normalized(run.get("provider")) != expected_provider: + continue + if normalized(run.get("model")) != expected_model: + continue + if not observed_usage(run): + continue + if not positive_duration(run.get("duration_ms")): + continue + if not SHA256_RE.match(str(run.get("output_sha256", "")).strip()): + continue + matches.append(run) + return matches + + +def validate_provider_execution_report( + execution: dict[str, Any], + provenance: dict[str, Any], + errors: list[str], +) -> None: + execution_summary = summary(execution) + runs = run_rows(execution) + validate_no_raw_fields(execution, errors) + add_error(errors, execution.get("ok") is True, "provider-holdout output execution report ok must be true") + add_error(errors, bool(runs), "provider-holdout output execution report must include run rows") + + computed_model_runs = sum(1 for run in runs if run.get("model_executed") is True) + computed_timing = sum(1 for run in runs if run.get("duration_ms") is not None) + computed_token_observed = sum(1 for run in runs if isinstance(run.get("usage"), dict) and run["usage"].get("estimated") is False) + computed_failures = sum(1 for run in runs if run.get("status") != "pass") + add_error( + errors, + real_int(execution_summary.get("model_executed_count")) == computed_model_runs, + "provider-holdout output execution summary.model_executed_count must match model-executed run rows", + ) + add_error( + errors, + real_int(execution_summary.get("timing_observed_count")) == computed_timing, + "provider-holdout output execution summary.timing_observed_count must match timed run rows", + ) + add_error( + errors, + real_int(execution_summary.get("token_observed_count")) == computed_token_observed, + "provider-holdout output execution summary.token_observed_count must match non-estimated usage rows", + ) + add_error( + errors, + real_int(execution_summary.get("failure_count")) == computed_failures, + "provider-holdout output execution summary.failure_count must match failed run rows", + ) + add_error( + errors, + bool(real_int(execution_summary.get("model_executed_count")) and execution_summary["model_executed_count"] > 0), + "provider-holdout output execution summary.model_executed_count must be >0", + ) + add_error( + errors, + bool(real_int(execution_summary.get("timing_observed_count")) and execution_summary["timing_observed_count"] > 0), + "provider-holdout output execution summary.timing_observed_count must be >0", + ) + add_error( + errors, + bool(real_int(execution_summary.get("token_observed_count")) and execution_summary["token_observed_count"] > 0), + "provider-holdout output execution summary.token_observed_count must be >0", + ) + add_error( + errors, + execution_summary.get("failure_count") == 0, + "provider-holdout output execution summary.failure_count must be 0", + ) + add_error( + errors, + bool(provider_model_runs(execution, provenance)), + "provider-holdout output execution runs must include a passing model run with matching provider, model, timing, non-estimated usage, and output hash", + ) diff --git a/scripts/world_class_repair_checklist.py b/scripts/world_class_repair_checklist.py new file mode 100644 index 0000000..9e8d913 --- /dev/null +++ b/scripts/world_class_repair_checklist.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +"""Build machine-readable repair checklists for world-class submission kits.""" + +from collections import defaultdict +import re +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Shared by submission kit generation to turn readiness blockers into actionable repair rows." + + +OWNER_BY_EVIDENCE = { + "provider-holdout": "operator with provider credentials", + "human-adjudication": "human reviewer", + "native-permission-enforcement": "target client or installer integrator", + "native-client-telemetry": "Browser/Chrome/IDE/provider client integrator", +} + +REPAIR_PHASES = { + "unknown-key": ("select-evidence", 5), + "draft": ("prepare-draft", 10), + "precheck": ("unblock-access", 20), + "artifact": ("attach-artifacts", 30), + "source-check": ("collect-source", 40), +} + + +def _by_key(rows: list[dict[str, Any]], key_name: str = "evidence_key") -> dict[str, list[dict[str, Any]]]: + grouped: dict[str, list[dict[str, Any]]] = defaultdict(list) + for row in rows: + key = str(row.get(key_name, "")).strip() + if key: + grouped[key].append(row) + return dict(grouped) + + +def _slug(value: str) -> str: + slug = re.sub(r"[^a-zA-Z0-9_.-]+", "-", value.strip()).strip("-") + return slug or "repair" + + +def repair_verification_command(evidence_key: str, repair_type: str) -> str: + preflight = "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions" + if repair_type == "draft": + return ( + "python3 scripts/yao.py world-class-submission-kit . " + f"--evidence-key {evidence_key} --output-dir evidence/world_class/submissions" + ) + if repair_type == "artifact": + return ( + "python3 scripts/yao.py world-class-submission-kit . " + f"--evidence-key {evidence_key} --output-dir evidence/world_class/submissions --prefill-artifacts" + ) + if repair_type == "unknown-key": + return "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + if evidence_key == "provider-holdout" and repair_type == "source-check": + return ( + "python3 scripts/yao.py output-exec --provider-runner " + "--provider-model --timeout-seconds 60 && " + + preflight + ) + if evidence_key == "human-adjudication" and repair_type == "source-check": + return "python3 scripts/yao.py output-review && " + preflight + if evidence_key == "native-permission-enforcement" and repair_type == "source-check": + return "python3 scripts/yao.py runtime-permissions . --package-dir dist && " + preflight + if evidence_key == "native-client-telemetry" and repair_type == "source-check": + return ( + "python3 scripts/yao.py telemetry-import . " + "--input-jsonl .yao/telemetry_spool/external_events.jsonl && " + + preflight + ) + return preflight + + +def _repair_row( + *, + evidence_key: str, + repair_type: str, + target: str, + status: str, + blocking_reason: str, + next_action: str, + source: str, +) -> dict[str, Any]: + phase, priority = REPAIR_PHASES.get(repair_type, ("repair", 90)) + return { + "action_id": _slug(f"{evidence_key}:{repair_type}:{target}"), + "evidence_key": evidence_key, + "repair_type": repair_type, + "target": target, + "phase": phase, + "priority": priority, + "owner": OWNER_BY_EVIDENCE.get(evidence_key, "release reviewer"), + "status": status, + "blocking_reason": blocking_reason, + "next_action": next_action, + "verification_command": repair_verification_command(evidence_key, repair_type), + "source": source, + "counts_as_completion": False, + } + + +def sort_repair_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]: + return sorted( + rows, + key=lambda row: ( + int(row.get("priority", 90) or 90), + str(row.get("evidence_key", "")), + str(row.get("target", "")), + ), + ) + + +def build_repair_checklist( + evidence_items: list[dict[str, Any]], + files: list[dict[str, Any]], + artifact_checklist: list[dict[str, Any]], + source_checklist: list[dict[str, Any]], + unknown_keys: list[str], +) -> list[dict[str, Any]]: + """Return concrete repair rows for every draft, artifact, and source blocker.""" + files_by_key = _by_key(files) + artifacts_by_key = _by_key(artifact_checklist) + sources_by_key = _by_key(source_checklist) + rows: list[dict[str, Any]] = [] + known_keys = [str(item.get("evidence_key", "")).strip() for item in evidence_items] + + for key in known_keys: + if not key: + continue + draft_rows = files_by_key.get(key, []) + if not draft_rows: + rows.append( + _repair_row( + evidence_key=key, + repair_type="draft", + target=key, + status="blocked", + blocking_reason="Submission draft was not generated.", + next_action="Run the submission kit command again for this evidence key.", + source="files", + ) + ) + for draft in draft_rows: + draft_status = str(draft.get("status", "")).strip() + if draft_status not in {"written", "exists"}: + target = str(draft.get("output_path") or draft.get("template_path") or key) + errors = draft.get("errors", []) + error_text = "; ".join(str(error) for error in errors) if isinstance(errors, list) else "" + rows.append( + _repair_row( + evidence_key=key, + repair_type="draft", + target=target, + status="blocked", + blocking_reason=error_text or f"Draft status is {draft_status or 'unknown'}.", + next_action="Fix template generation before asking for ledger review.", + source="files", + ) + ) + + for artifact in artifacts_by_key.get(key, []): + if artifact.get("artifact_ref_ready"): + continue + target = str(artifact.get("path") or artifact.get("source_pattern") or key) + artifact_status = str(artifact.get("status", "missing")) + role = str(artifact.get("artifact_role", "supporting-evidence")) + if artifact.get("concrete_reference_required"): + action = "Replace the glob with concrete files, then reference the generated SHA-256 digests." + elif artifact.get("submission_ref_required"): + action = "Create the required submission artifact or update artifact_refs to a concrete existing aggregate file." + else: + action = "Add the supporting artifact if it is needed for reviewer audit." + rows.append( + _repair_row( + evidence_key=key, + repair_type="artifact", + target=target, + status="blocked", + blocking_reason=f"{role} artifact is {artifact_status}.", + next_action=action, + source="artifact_checklist", + ) + ) + + for source in sources_by_key.get(key, []): + if source.get("status") == "pass": + continue + field = str(source.get("field") or source.get("label") or key) + rows.append( + _repair_row( + evidence_key=key, + repair_type="source-check", + target=field, + status="blocked", + blocking_reason=f"Current value {source.get('actual')!r} does not satisfy {source.get('expected')!r}.", + next_action=str(source.get("next_action") or "Collect the required source evidence."), + source="source_checklist", + ) + ) + + for key in unknown_keys: + rows.append( + _repair_row( + evidence_key=key, + repair_type="unknown-key", + target=key, + status="blocked", + blocking_reason="Requested evidence key is not present in the operator checklist.", + next_action="Use one of the evidence keys listed by world-class-intake.", + source="unknown_evidence_keys", + ) + ) + + return sort_repair_rows(rows) + + +def build_preflight_repair_checklist(items: list[dict[str, Any]]) -> list[dict[str, Any]]: + """Return repair rows from preflight precheck and source-check blockers.""" + rows: list[dict[str, Any]] = [] + for item in items: + key = str(item.get("evidence_key", "")).strip() + if not key: + continue + for precheck in item.get("prechecks", []): + if not isinstance(precheck, dict): + continue + if precheck.get("required") is not True or precheck.get("status") == "pass": + continue + target = str(precheck.get("key") or precheck.get("label") or key) + rows.append( + _repair_row( + evidence_key=key, + repair_type="precheck", + target=target, + status="blocked", + blocking_reason=( + f"Required {precheck.get('kind', 'precheck')} precheck is " + f"{precheck.get('status', 'unknown')}." + ), + next_action=str(precheck.get("next_action") or "Complete the required preflight action."), + source="prechecks", + ) + ) + for source in item.get("source_checklist", []): + if not isinstance(source, dict): + continue + if source.get("status") == "pass": + continue + target = str(source.get("field") or source.get("label") or key) + rows.append( + _repair_row( + evidence_key=key, + repair_type="source-check", + target=target, + status="blocked", + blocking_reason=( + f"Current value {source.get('actual')!r} does not satisfy " + f"{source.get('expected')!r}." + ), + next_action=str(source.get("next_action") or "Collect the required source evidence."), + source="source_checklist", + ) + ) + return sort_repair_rows(rows) + + +def summarize_repair_checklist(rows: list[dict[str, Any]]) -> dict[str, Any]: + blocked_count = sum(1 for row in rows if row.get("status") != "ready") + phase_counts: dict[str, int] = {} + for row in rows: + phase = str(row.get("phase", "repair")) + phase_counts[phase] = phase_counts.get(phase, 0) + 1 + next_row = sort_repair_rows(rows)[0] if rows else {} + return { + "repair_checklist_count": len(rows), + "repair_blocked_count": blocked_count, + "repair_ready_count": len(rows) - blocked_count, + "repair_phase_counts": phase_counts, + "next_repair_action_id": next_row.get("action_id", ""), + "next_repair_phase": next_row.get("phase", ""), + "next_repair_owner": next_row.get("owner", ""), + "next_repair_command": next_row.get("verification_command", ""), + "repair_counts_as_completion": False, + } diff --git a/scripts/world_class_source_checks.py b/scripts/world_class_source_checks.py new file mode 100644 index 0000000..a05580b --- /dev/null +++ b/scripts/world_class_source_checks.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +"""Shared source-evidence checks for world-class evidence workflows.""" + +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by world-class evidence reports to keep source-evidence readiness checks consistent." + + +SOURCE_CHECK_SPECS = { + "provider-holdout": [ + ("Provider model run", "model_executed_count", ">0", "Run provider-backed output-exec with real credentials."), + ("Timing observed", "timing_observed_count", ">0", "Provider execution should record timing metadata."), + ("Token usage observed", "token_observed_count", ">0", "Provider execution should return non-estimated token usage."), + ], + "human-adjudication": [ + ("Review pairs exist", "pair_count", ">0", "Generate the blind A/B review pack."), + ("No pending decisions", "pending_count", "==0", "Record a reviewer choice and reason for every pair."), + ("Judgments complete", "judgment_count", "==pair_count", "Every pair needs one valid human judgment."), + ("No invalid decisions", "invalid_decision_count", "==0", "Fix malformed winner/confidence entries."), + ("Reviewer metadata", "reviewer_metadata_present", "true", "Record reviewer and reviewed_at before adjudication can count."), + ("Reason required", "reason_required", "true", "Keep reason mandatory for every imported or direct reviewer decision."), + ("Blind review attested", "blind_review_attested", "true", "Set reviewer_attestation only after choices are completed before opening the answer key."), + ("Raw content attested", "raw_content_excluded_attested", "true", "Attest that reviewer decisions exclude raw prompts, outputs, transcripts, messages, and private user content."), + ("Raw content blocked", "raw_content_allowed", "false", "Adjudication evidence should store prompt hashes and reviewer metadata, not raw prompts, outputs, transcripts, or messages."), + ("Human evidence ready", "ready_for_human_evidence", "true", "Complete all reviewer decisions with metadata and rationale, plus blind-review attestation and integrity fingerprints."), + ], + "native-permission-enforcement": [ + ("Native enforcement", "native_enforcement_count", ">0", "Collect real target-client or external runtime guard proof."), + ("Probe failures", "failure_count", "==0", "Runtime permission probes must stay clean."), + ("Installer support", "installer_enforcement_ready", "true", "Installer enforcement is supporting evidence, not native proof."), + ], + "native-client-telemetry": [ + ("External events", "external_source_events", ">0", "Import at least one metadata-only event from a real client."), + ("Adoption sample", "adoption_sample_count", ">0", "Telemetry must include adoption outcome evidence."), + ("Raw content blocked", "raw_content_allowed", "false", "Telemetry must stay metadata-only."), + ], +} + + +def source_check_passed(actual: Any, expected: str, observed_state: dict[str, Any]) -> bool: + if expected == ">0": + return isinstance(actual, (int, float)) and actual > 0 + if expected == "==0": + return actual == 0 + if expected == "true": + return actual is True + if expected == "false": + return actual is False + if expected == "==pair_count": + return actual == observed_state.get("pair_count") and isinstance(actual, (int, float)) and actual > 0 + return False + + +def item_evidence_key(item: dict[str, Any]) -> str: + return str(item.get("evidence_key") or item.get("key") or "") + + +def item_observed_state(item: dict[str, Any]) -> dict[str, Any]: + observed = item.get("observed_state", {}) + return observed if isinstance(observed, dict) else {} + + +def item_source_accepted(item: dict[str, Any], observed_state: dict[str, Any]) -> bool: + return item.get("source_accepted") is True or observed_state.get("accepted") is True + + +def build_source_checklist(items: list[dict[str, Any]]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + for item in items: + key = item_evidence_key(item) + observed_state = item_observed_state(item) + for label, field, expected, next_action in SOURCE_CHECK_SPECS.get(key, []): + actual = observed_state.get(field) + passed = source_check_passed(actual, expected, observed_state) + rows.append( + { + "evidence_key": key, + "label": label, + "field": field, + "expected": expected, + "actual": actual, + "status": "pass" if passed else "blocked", + "source_accepted": item_source_accepted(item, observed_state), + "next_action": next_action, + } + ) + return rows + + +def summarize_source_checklist(rows: list[dict[str, Any]]) -> dict[str, int]: + pass_count = sum(1 for item in rows if item.get("status") == "pass") + blocked_count = sum(1 for item in rows if item.get("status") != "pass") + return { + "source_check_count": len(rows), + "source_pass_count": pass_count, + "source_blocked_count": blocked_count, + } diff --git a/scripts/world_class_submission_kit_rendering.py b/scripts/world_class_submission_kit_rendering.py new file mode 100644 index 0000000..ae0da58 --- /dev/null +++ b/scripts/world_class_submission_kit_rendering.py @@ -0,0 +1,576 @@ +#!/usr/bin/env python3 +"""Render Markdown and HTML artifacts for world-class submission kits.""" + +from typing import Any + +from html_rendering import html_text + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Shared renderer for world-class submission kit Markdown and HTML artifacts." + + +def render_readme(report: dict[str, Any]) -> str: + commands = report["commands"] + lines = [ + "# World-Class Evidence Submission Kit", + "", + f"Generated at: `{report['generated_at']}`", + "", + "This kit contains editable drafts for human and external evidence packets. Drafts are not accepted evidence.", + "", + "## Workflow", + "", + "1. Run the real provider, human review, native permission, or native client telemetry work first.", + "2. Edit the matching JSON draft with only aggregate artifact references and provenance metadata.", + "3. Set `template_only` to `false` only after real evidence exists.", + "4. Set attestation booleans truthfully; do not include credentials, raw prompts, raw outputs, transcripts, notes, or private user content.", + "5. Validate the packet before asking the ledger reviewer to set `attestation.ledger_reviewer`, `attestation.ledger_reviewed_at`, and `attestation.ledger_reviewer_approved` truthfully.", + "6. Optional artifact prefill only inserts SHA-256 digests for current local aggregate artifacts; it does not mark a draft as real evidence.", + "", + "## Commands", + "", + f"- validate intake: `{commands['validate_intake']}`", + f"- review submission: `{commands['review_submission']}`", + f"- refresh ledger: `{commands['refresh_ledger']}`", + f"- guard public claims: `{commands['guard_claim']}`", + "", + "## Operator Handoff", + "", + "Follow these steps in order when handing the kit from operator to reviewer. Handoff rows are procedural and never count as completion evidence.", + "", + "| Step | Status | Command | Completion signal |", + "| --- | --- | --- | --- |", + ] + for item in report.get("operator_handoff", []): + command = item.get("command") or "manual" + lines.append( + f"| `{item['step_id']}` | `{item['status']}` | `{command}` | {item['completion_signal']} |" + ) + lines.extend( + [ + "", + "## Phase Queue", + "", + "This queue groups repair rows by execution phase so operators can clear access, artifact, and source blockers in order. Queue rows are procedural guidance only.", + "", + "| Phase | Status | Rows | Owners | Next action | Verify |", + "| --- | --- | ---: | --- | --- | --- |", + ] + ) + phase_queue = report.get("phase_queue", []) + if phase_queue: + for item in phase_queue: + owners = ", ".join(str(owner) for owner in item.get("owners", [])) or "n/a" + lines.append( + f"| `{item['phase']}` | `{item['status']}` | `{item['row_count']}` | {owners} | " + f"{item['next_action']} | `{item['verification_command']}` |" + ) + else: + lines.append("| `all` | `ready` | `0` | n/a | No phase blockers were generated. | `n/a` |") + lines.extend( + [ + "", + "## Drafts", + "", + "| Evidence | Draft | Status | Prefilled refs |", + "| --- | --- | --- | ---: |", + ] + ) + for item in report["files"]: + lines.append( + f"| `{item['evidence_key']}` | `{item['output_path']}` | `{item['status']}` | `{item.get('prefilled_artifact_ref_count', 0)}` |" + ) + lines.extend( + [ + "", + "## Evidence Matrix", + "", + "This matrix combines draft, artifact, and source-check readiness into one operator action list. Matrix rows are guidance only; they do not count as completion evidence.", + "", + "| Evidence | Stage | Draft | Submission refs | Supporting assets | Source checks | Next action |", + "| --- | --- | --- | ---: | ---: | ---: | --- |", + ] + ) + for item in report.get("evidence_matrix", []): + lines.append( + f"| `{item['evidence_key']}` | `{item['stage']}` | `{item['draft_status']}` | " + f"`{item.get('submission_ref_ready_count', 0)}/{item.get('submission_ref_total_count', 0)}` | " + f"`{item.get('supporting_artifact_ready_count', 0)}/{item.get('supporting_artifact_total_count', 0)}` | " + f"`{item['source_pass_count']}/{item['source_check_count']}` | {item['next_action']} |" + ) + lines.extend( + [ + "", + "## Repair Checklist", + "", + "This checklist turns every draft, artifact, and source blocker into a concrete repair row. Repair rows are procedural guidance and do not count as completion evidence.", + "", + "| Evidence | Type | Target | Status | Next action |", + "| --- | --- | --- | --- | --- |", + ] + ) + repair_rows = report.get("repair_checklist", []) + if repair_rows: + for item in repair_rows: + lines.append( + f"| `{item['evidence_key']}` | `{item['repair_type']}` | `{item['target']}` | " + f"`{item['status']}` | {item['next_action']} |" + ) + else: + lines.append("| `all` | `none` | `n/a` | `ready` | No repair rows were generated. |") + lines.extend(["", "## Execution Runbook", ""]) + for item in report.get("evidence_items", []): + must_collect = item.get("must_collect", {}) if isinstance(item.get("must_collect", {}), dict) else {} + runbook = must_collect.get("runbook", []) + lines.extend(["", f"### {item.get('label', item.get('evidence_key', 'Evidence'))}", ""]) + if runbook: + for step in runbook: + lines.append(f"- `{step}`" if str(step).startswith("python3 ") or "=" in str(step) else f"- {step}") + else: + lines.append("- No source runbook listed.") + lines.extend( + [ + "", + "## Artifact Checklist", + "", + "Use these paths and SHA-256 digests when filling `artifact_refs`. Glob patterns are expanded into concrete files; submissions must reference concrete paths, not globs.", + "", + "| Evidence | Role | Path | Status | SHA-256 |", + "| --- | --- | --- | --- | --- |", + ] + ) + for item in report.get("artifact_checklist", []): + digest = item.get("sha256") or "n/a" + lines.append( + f"| `{item['evidence_key']}` | `{item.get('artifact_role', 'supporting-evidence')}` | " + f"`{item['path']}` | `{item['status']}` | `{digest}` |" + ) + lines.extend( + [ + "", + "## Source Evidence Snapshot", + "", + "These checks explain why a draft is not ready for ledger acceptance yet. They mirror current aggregate reports and do not accept evidence by themselves.", + "", + "| Evidence | Check | Current | Expected | Status |", + "| --- | --- | --- | --- | --- |", + ] + ) + for item in report.get("source_checklist", []): + lines.append( + f"| `{item['evidence_key']}` | {item['label']} | `{item['actual']}` | `{item['expected']}` | `{item['status']}` |" + ) + lines.extend( + [ + "", + "## Anti-Overclaim", + "", + "- This kit never marks ledger evidence as accepted.", + "- Planned work, metadata fallback, pending review, and local command-runner output remain non-evidence.", + "- A valid intake packet means ready for ledger review, not world-class completion.", + ] + ) + return "\n".join(lines).rstrip() + "\n" + + +def render_html_list(values: list[Any], empty: str) -> str: + if not values: + return f"
  • {html_text(empty)}
  • " + return "".join(f"
  • {html_text(value)}
  • " for value in values) + + +def render_html_commands(commands: dict[str, str]) -> str: + return "".join( + f"
  • {html_text(label.replace('_', ' '))}{html_text(command)}
  • " + for label, command in commands.items() + ) + + +def render_html_files(files: list[dict[str, Any]]) -> str: + if not files: + return '

    No submission drafts were requested.

    ' + return "".join( + """ +
    +
    + {status} +

    {key}

    +
    +
    +
    Template
    {template}
    +
    Draft
    {output}
    +
    Prefill
    {prefill} artifact refs
    +
    + {errors} +
    + """.format( + status=html_text(item.get("status", "")), + key=html_text(item.get("evidence_key", "")), + template=html_text(item.get("template_path", "")), + output=html_text(item.get("output_path", "")), + prefill=html_text(item.get("prefilled_artifact_ref_count", 0)), + errors=( + '
      ' + + render_html_list(item.get("errors", []), "No errors.") + + "
    " + if item.get("errors") + else "" + ), + ) + for item in files + ) + + +def render_html_artifact_checklist(items: list[dict[str, Any]]) -> str: + if not items: + return '

    No required artifacts were listed for the requested evidence.

    ' + return "".join( + """ +
    +
    + {key} +

    {path}

    +
    +
    +
    Pattern
    {pattern}
    +
    Role
    {role}
    +
    Status
    {status}
    +
    SHA-256
    {sha}
    +
    +
    + """.format( + status=html_text(item.get("status", "")), + key=html_text(item.get("evidence_key", "")), + path=html_text(item.get("path", "")), + pattern=html_text(item.get("source_pattern", "")), + role=html_text(item.get("artifact_role", "supporting-evidence")), + sha=html_text(item.get("sha256") or "n/a"), + ) + for item in items + ) + + +def render_html_source_checklist(items: list[dict[str, Any]]) -> str: + if not items: + return '

    No source checks were listed for the requested evidence.

    ' + return "".join( + """ +
    +
    + {key} +

    {label}

    +
    +
    +
    Field
    {field}
    +
    Current
    {actual}
    +
    Expected
    {expected}
    +
    Action
    {action}
    +
    +
    + """.format( + status=html_text(item.get("status", "")), + key=html_text(item.get("evidence_key", "")), + label=html_text(item.get("label", "")), + field=html_text(item.get("field", "")), + actual=html_text(item.get("actual", "")), + expected=html_text(item.get("expected", "")), + action=html_text(item.get("next_action", "")), + ) + for item in items + ) + + +def render_html_matrix(items: list[dict[str, Any]]) -> str: + if not items: + return '

    No evidence matrix rows were generated.

    ' + return "".join( + """ +
    +
    + {stage} +

    {key}

    +
    +
    +
    Draft
    {draft}
    +
    Submission refs
    {submission_ref_ready}/{submission_ref_total} ready
    +
    Supporting assets
    {supporting_ready}/{supporting_total} ready
    +
    Source
    {source_pass}/{source_total} pass
    +
    Owner
    {owner}
    +
    +

    {action}

    +
    + """.format( + stage=html_text(item.get("stage", "")), + key=html_text(item.get("evidence_key", "")), + draft=html_text(item.get("draft_status", "")), + submission_ref_ready=html_text(item.get("submission_ref_ready_count", 0)), + submission_ref_total=html_text(item.get("submission_ref_total_count", 0)), + supporting_ready=html_text(item.get("supporting_artifact_ready_count", 0)), + supporting_total=html_text(item.get("supporting_artifact_total_count", 0)), + source_pass=html_text(item.get("source_pass_count", 0)), + source_total=html_text(item.get("source_check_count", 0)), + owner=html_text(item.get("owner", "")), + action=html_text(item.get("next_action", "")), + ) + for item in items + ) + + +def render_html_repair_checklist(items: list[dict[str, Any]]) -> str: + if not items: + return '

    No repair rows were generated.

    ' + return "".join( + """ +
    +
    + {key} · {repair_type} +

    {target}

    +
    +
    +
    Priority
    {priority}
    +
    Phase
    {phase}
    +
    Owner
    {owner}
    +
    Status
    {status}
    +
    Reason
    {reason}
    +
    Action
    {action}
    +
    Verify
    {verify}
    +
    Evidence
    does not count as completion
    +
    +
    + """.format( + status=html_text(item.get("status", "")), + key=html_text(item.get("evidence_key", "")), + repair_type=html_text(item.get("repair_type", "")), + target=html_text(item.get("target", "")), + priority=html_text(item.get("priority", "")), + phase=html_text(item.get("phase", "")), + owner=html_text(item.get("owner", "")), + reason=html_text(item.get("blocking_reason", "")), + action=html_text(item.get("next_action", "")), + verify=html_text(item.get("verification_command", "")), + ) + for item in items + ) + + +def render_html_phase_queue(items: list[dict[str, Any]]) -> str: + if not items: + return '

    No phase queue rows were generated.

    ' + return "".join( + """ +
    +
    + #{priority} · {status} +

    {label}

    +
    +
    +
    Phase
    {phase}
    +
    Rows
    {blocked}/{total} blocked
    +
    Owners
    {owners}
    +
    Evidence
    {evidence}
    +
    Next
    {action}
    +
    Verify
    {verify}
    +
    Counts
    does not count as completion
    +
    +
    + """.format( + status=html_text(item.get("status", "")), + priority=html_text(item.get("priority", "")), + label=html_text(item.get("label", "")), + phase=html_text(item.get("phase", "")), + blocked=html_text(item.get("blocked_count", 0)), + total=html_text(item.get("row_count", 0)), + owners=html_text(", ".join(str(owner) for owner in item.get("owners", [])) or "n/a"), + evidence=html_text(", ".join(str(key) for key in item.get("evidence_keys", [])) or "n/a"), + action=html_text(item.get("next_action", "")), + verify=html_text(item.get("verification_command", "")), + ) + for item in items + ) + + +def render_html_handoff(items: list[dict[str, Any]]) -> str: + if not items: + return '

    No operator handoff steps were generated.

    ' + return "".join( + """ +
    +
    + {status} +

    {label}

    +
    +
    +
    Step
    {step}
    +
    Command
    {command}
    +
    Signal
    {signal}
    +
    Evidence
    {counts}
    +
    +

    {blocking}

    +
    + """.format( + status=html_text(item.get("status", "")), + label=html_text(item.get("label", "")), + step=html_text(item.get("step_id", "")), + command=html_text(item.get("command") or "manual"), + signal=html_text(item.get("completion_signal", "")), + counts="does not count as completion" if item.get("counts_as_completion") is False else "review required", + blocking=html_text(item.get("blocking_condition", "")), + ) + for item in items + ) + + +def render_html_item(item: dict[str, Any]) -> str: + must_collect = item.get("must_collect", {}) if isinstance(item.get("must_collect", {}), dict) else {} + runbook = must_collect.get("runbook", []) + return f""" +
    +
    + {html_text(item.get('category', ''))} · {html_text(item.get('readiness', ''))} +

    {html_text(item.get('label', item.get('evidence_key', '')))}

    +
    +

    {html_text(item.get('blocking_reason', ''))}

    +
    +
    Owner
    {html_text(item.get('owner', ''))}
    +
    Evidence
    {html_text(item.get('evidence_key', ''))}
    +
    Submission
    {html_text(item.get('submission_path', ''))}
    +
    +
    +

    Execution Runbook

    +
      {render_html_list(runbook, 'No source runbook listed.')}
    +
    +
    +
    +

    Must Collect

    +
      {render_html_list(must_collect.get('provenance_requirements', []), 'No provenance requirements listed.')}
    +
    +
    +

    Pass Checks

    +
      {render_html_list(must_collect.get('success_checks', []), 'No success checks listed.')}
    +
    +
    +

    Privacy

    +
      {render_html_list(must_collect.get('privacy_contract', []), 'No privacy contract listed.')}
    +
    +
    +
    + """ + + +def render_html(report: dict[str, Any]) -> str: + summary = report["summary"] + artifact_ready = summary.get("artifact_ready_count", 0) + artifact_total = summary.get("artifact_checklist_count", 0) + submission_ref_ready = summary.get("submission_ref_ready_count", 0) + submission_ref_total = summary.get("submission_ref_count", 0) + stats = [ + ("Requested", summary["requested_count"]), + ("Written", summary["written_count"]), + ("Existing", summary["existing_count"]), + ("Skipped", summary["skipped_count"]), + ("Submit refs", f"{submission_ref_ready}/{submission_ref_total}"), + ("Support", f"{artifact_ready - submission_ref_ready}/{artifact_total - submission_ref_total}"), + ("Prefilled", summary.get("artifact_ref_prefill_count", 0)), + ] + stat_html = "".join( + f"
    {html_text(label)}{html_text(value)}
    " + for label, value in stats + ) + evidence_html = "".join(render_html_item(item) for item in report.get("evidence_items", [])) + matrix_html = render_html_matrix(report.get("evidence_matrix", [])) + repair_html = render_html_repair_checklist(report.get("repair_checklist", [])) + phase_queue_html = render_html_phase_queue(report.get("phase_queue", [])) + handoff_html = render_html_handoff(report.get("operator_handoff", [])) + artifact_html = render_html_artifact_checklist(report.get("artifact_checklist", [])) + source_html = render_html_source_checklist(report.get("source_checklist", [])) + return f""" + + + + + World-Class Evidence Submission Kit + + + + +
    +
    + Evidence Intake +

    World-Class Evidence Submission Kit

    +

    Use this cockpit to prepare human and external evidence packets. Drafts are not accepted evidence, artifact prefill only inserts local SHA-256 digests, and this page never changes the ledger result.

    +
    {stat_html}
    +
    +
    +

    Workflow

    1. Run the real provider, human review, native permission, or native client telemetry work first.
    2. Edit the matching JSON draft with aggregate artifact references and provenance metadata.
    3. Set template_only to false only after real evidence exists.
    4. Use prefilled SHA-256 values as convenience data, not evidence acceptance.
    5. Validate intake, ask the ledger reviewer to approve explicitly, refresh the ledger, then guard public claims.
    + +
    +

    Operator Handoff

    These ordered steps make the operator-to-reviewer handoff auditable. They are procedural guardrails and never count as world-class evidence.

    {handoff_html}
    +

    Phase Queue

    Repair rows are grouped into execution phases so operators can clear the first blocker before moving to later evidence work. Queue rows remain procedural and never count as accepted evidence.

    {phase_queue_html}
    +

    Evidence Matrix

    The matrix separates submission artifact_refs from supporting assets, then combines draft status, source checks, and the next operator action. It is guidance only and never counts as accepted evidence.

    {matrix_html}
    +

    Repair Checklist

    Each row names one concrete blocker and the next action required before ledger review. This checklist does not count as completion evidence.

    {repair_html}
    +

    Drafts

    {render_html_files(report['files'])}
    +

    Artifact Checklist

    Rows marked submission-ref are the paths expected in artifact_refs. Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into the submission. Glob patterns are expanded for operator convenience only.

    {artifact_html}
    +

    Source Evidence Snapshot

    This section shows current aggregate source checks. It explains remaining blockers without changing the ledger.

    {source_html}
    +

    Evidence Requirements

    {evidence_html}
    +

    Safety Boundary

    • Drafts never count as accepted ledger evidence.
    • Valid intake means ready for ledger review, not world-class completion.
    • Do not include credentials, raw prompts, raw outputs, transcripts, notes, or private user content.
    +
    + + +""" diff --git a/scripts/world_class_submission_matrix.py b/scripts/world_class_submission_matrix.py new file mode 100644 index 0000000..943b5ef --- /dev/null +++ b/scripts/world_class_submission_matrix.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +"""Build operator-facing readiness matrices for world-class submission kits.""" + +from collections import defaultdict +from typing import Any + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Shared by submission kit rendering to summarize draft, artifact, and source-check readiness." + + +def _by_key(rows: list[dict[str, Any]], key_name: str = "evidence_key") -> dict[str, list[dict[str, Any]]]: + grouped: dict[str, list[dict[str, Any]]] = defaultdict(list) + for row in rows: + key = str(row.get(key_name, "")).strip() + if key: + grouped[key].append(row) + return dict(grouped) + + +def _draft_status(files_by_key: dict[str, list[dict[str, Any]]], key: str) -> tuple[str, str]: + rows = files_by_key.get(key, []) + if not rows: + return "missing", "" + row = rows[0] + return str(row.get("status", "missing")), str(row.get("output_path", "")) + + +def _first_blocked_action(source_rows: list[dict[str, Any]]) -> str: + for row in source_rows: + if row.get("status") != "pass": + action = str(row.get("next_action", "")).strip() + if action: + return action + return "" + + +def _stage( + *, + draft_status: str, + artifact_missing_count: int, + source_blocked_count: int, + invalid_draft: bool, +) -> str: + if draft_status == "skipped" or invalid_draft: + return "fix-draft" + if draft_status == "missing": + return "prepare-draft" + if source_blocked_count: + return "collect-source" + if artifact_missing_count: + return "fix-artifacts" + return "validate-packet" + + +def _next_action( + *, + stage: str, + draft_path: str, + first_blocked_action: str, + artifact_missing_count: int, +) -> str: + if stage == "fix-draft": + return "Fix skipped or invalid draft generation before evidence review." + if stage == "prepare-draft": + return "Generate the evidence draft, then keep template_only true until real evidence exists." + if stage == "collect-source": + return first_blocked_action or "Complete blocked source checks before ledger review." + if stage == "fix-artifacts": + return f"Resolve {artifact_missing_count} missing artifact reference(s) or replace globs with concrete paths." + if draft_path: + return f"Edit {draft_path}, set real provenance fields, validate intake, then refresh the ledger." + return "Validate intake, refresh the ledger, then run the public claim guard." + + +def build_evidence_matrix( + evidence_items: list[dict[str, Any]], + files: list[dict[str, Any]], + artifact_checklist: list[dict[str, Any]], + source_checklist: list[dict[str, Any]], +) -> list[dict[str, Any]]: + """Combine draft, artifact, and source-check readiness into one operator matrix.""" + files_by_key = _by_key(files) + artifacts_by_key = _by_key(artifact_checklist) + sources_by_key = _by_key(source_checklist) + matrix: list[dict[str, Any]] = [] + for item in evidence_items: + key = str(item.get("evidence_key", "")).strip() + if not key: + continue + artifact_rows = artifacts_by_key.get(key, []) + source_rows = sources_by_key.get(key, []) + draft_status, draft_path = _draft_status(files_by_key, key) + artifact_ready_count = sum(1 for row in artifact_rows if row.get("artifact_ref_ready")) + artifact_missing_count = len(artifact_rows) - artifact_ready_count + submission_ref_rows = [row for row in artifact_rows if row.get("submission_ref_required")] + supporting_rows = [row for row in artifact_rows if not row.get("submission_ref_required")] + submission_ref_ready_count = sum(1 for row in submission_ref_rows if row.get("artifact_ref_ready")) + supporting_artifact_ready_count = sum(1 for row in supporting_rows if row.get("artifact_ref_ready")) + source_pass_count = sum(1 for row in source_rows if row.get("status") == "pass") + source_blocked_count = len(source_rows) - source_pass_count + invalid_draft = draft_status not in {"written", "exists"} + stage = _stage( + draft_status=draft_status, + artifact_missing_count=artifact_missing_count, + source_blocked_count=source_blocked_count, + invalid_draft=invalid_draft, + ) + first_action = _first_blocked_action(source_rows) + matrix.append( + { + "evidence_key": key, + "label": item.get("label", key), + "category": item.get("category", ""), + "owner": item.get("owner", ""), + "stage": stage, + "draft_status": draft_status, + "draft_path": draft_path, + "artifact_ready_count": artifact_ready_count, + "artifact_total_count": len(artifact_rows), + "artifact_missing_count": artifact_missing_count, + "submission_ref_ready_count": submission_ref_ready_count, + "submission_ref_total_count": len(submission_ref_rows), + "submission_ref_missing_count": len(submission_ref_rows) - submission_ref_ready_count, + "supporting_artifact_ready_count": supporting_artifact_ready_count, + "supporting_artifact_total_count": len(supporting_rows), + "supporting_artifact_missing_count": len(supporting_rows) - supporting_artifact_ready_count, + "source_pass_count": source_pass_count, + "source_check_count": len(source_rows), + "source_blocked_count": source_blocked_count, + "next_action": _next_action( + stage=stage, + draft_path=draft_path, + first_blocked_action=first_action, + artifact_missing_count=artifact_missing_count, + ), + "counts_as_completion": False, + } + ) + return matrix + + +def summarize_evidence_matrix(matrix: list[dict[str, Any]]) -> dict[str, int]: + return { + "evidence_matrix_count": len(matrix), + "evidence_matrix_collect_source_count": sum(1 for item in matrix if item.get("stage") == "collect-source"), + "evidence_matrix_prepare_draft_count": sum(1 for item in matrix if item.get("stage") == "prepare-draft"), + "evidence_matrix_fix_artifacts_count": sum(1 for item in matrix if item.get("stage") == "fix-artifacts"), + "evidence_matrix_validate_packet_count": sum(1 for item in matrix if item.get("stage") == "validate-packet"), + "evidence_matrix_submission_ref_count": sum(int(item.get("submission_ref_total_count", 0)) for item in matrix), + "evidence_matrix_submission_ref_ready_count": sum( + int(item.get("submission_ref_ready_count", 0)) for item in matrix + ), + "evidence_matrix_supporting_artifact_count": sum( + int(item.get("supporting_artifact_total_count", 0)) for item in matrix + ), + "evidence_matrix_supporting_artifact_ready_count": sum( + int(item.get("supporting_artifact_ready_count", 0)) for item in matrix + ), + "evidence_matrix_counts_as_completion": 0, + } diff --git a/scripts/yao.py b/scripts/yao.py new file mode 100644 index 0000000..fc51f30 --- /dev/null +++ b/scripts/yao.py @@ -0,0 +1,563 @@ +#!/usr/bin/env python3 +import argparse +import json +import subprocess +from pathlib import Path + +from yao_cli_config import ( + baseline_compare_args, + local_output_runner_command, + resolve_promotion_target, + resolve_target, +) +from yao_cli_adaptation_commands import ( + command_adapt_apply, + command_adapt_propose, + command_adapt_scan, + command_daily_skillops, + command_weekly_curator, +) +from yao_cli_create_commands import command_init, command_quickstart +from yao_cli_distribution_commands import ( + command_compile_skill, + command_conformance, + command_install_simulate, + command_package, + command_package_verify, + command_registry_audit, + command_runtime_permissions, + command_skill_atlas, + command_skill_ir, + command_trust, + command_upgrade_check, +) +from yao_cli_output_commands import ( + command_output_eval, + command_output_execution, + command_output_review, + command_output_review_import, + command_output_review_kit, +) +from yao_cli_operator_commands import ( + command_install_status, + command_localized_doc_sync_check, + command_pr_review_report, +) +from yao_cli_parser import build_parser as build_cli_parser +from yao_cli_report_commands import ( + command_artifact_design_profile, + command_benchmark_reproducibility, + command_evidence_consistency, + command_github_benchmark_scan, + command_intent_confidence, + command_intent_dialogue, + command_iteration_directions, + command_output_risk_profile, + command_prompt_quality_profile, + command_reference_scan, + command_reference_synthesis, + command_report, + command_review_studio, + command_review_viewer, + command_skill_os2_audit, + command_skill_os2_coverage, + command_skill_interpretation, + command_skill_report, + command_system_model, + command_world_class_claim_guard, + command_world_class_evidence, + command_world_class_intake, + command_world_class_ledger, + command_world_class_preflight, + command_world_class_runbook, + command_world_class_submission_kit, + command_world_class_submission_review, +) +from yao_cli_runtime import ROOT, allow_report_status, run_adoption_drift_if_source_exists, run_script +from yao_cli_telemetry import add_telemetry_args, maybe_record_cli_event + + +def command_validate(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + runs = [ + run_script("validate_skill.py", [skill_dir]), + run_script("lint_skill.py", [skill_dir]), + run_script("governance_check.py", [skill_dir, *(["--require-manifest"] if args.require_manifest else [])]), + run_script("resource_boundary_check.py", [skill_dir]), + ] + report = { + "ok": all(item["ok"] for item in runs), + "skill_dir": skill_dir, + "steps": [ + { + "command": item["command"], + "ok": item["ok"], + "returncode": item["returncode"], + "payload": item["payload"], + } + for item in runs + ], + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 + + +def optimize_args_for_target(target_name: str, write: bool) -> list[str]: + target = resolve_target(target_name) + cmd = [ + "--description-file", + str(target["description_file"]), + "--baseline-description-file", + str(target["baseline_description_file"]), + "--semantic-config", + str(target["semantic_config"]), + "--dev-cases", + str(target["dev_cases"]), + "--holdout-cases", + str(target["holdout_cases"]), + "--blind-holdout-cases", + str(target["blind_holdout_cases"]), + "--adversarial-cases", + str(target["adversarial_cases"]), + "--title", + target["title"], + ] + if write: + cmd.extend(["--output-json", str(target["output_json"]), "--output-md", str(target["output_md"])]) + return cmd + + +def command_optimize_description(args: argparse.Namespace) -> int: + if args.target == "all": + result = run_script("run_description_optimization_suite.py", []) + else: + result = run_script("optimize_description.py", optimize_args_for_target(args.target, args.write)) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_promote_check(args: argparse.Namespace) -> int: + result = run_script("promotion_checker.py", []) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_python_compat(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + for path in args.path: + cmd.extend(["--path", path]) + if args.target_python: + cmd.extend(["--target-python", args.target_python]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + result = run_script("python_compat_check.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_architecture_audit(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.warn_lines is not None: + cmd.extend(["--warn-lines", str(args.warn_lines)]) + if args.block_lines is not None: + cmd.extend(["--block-lines", str(args.block_lines)]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + result = run_script("render_architecture_maintainability.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_feedback(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.note: + cmd.extend(["--note", args.note]) + cmd.extend(["--rating", str(args.rating), "--category", args.category, "--recommended-action", args.recommended_action]) + result = run_script("collect_feedback.py", cmd) + viewer = run_script("render_review_viewer.py", [skill_dir]) + report = { + "ok": result["ok"] and viewer["ok"], + "feedback": result["payload"] if result["payload"] is not None else result, + "review_viewer": viewer["payload"] if viewer["payload"] is not None else viewer, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 + + +def command_adoption_drift(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.events_jsonl: + cmd.extend(["--events-jsonl", args.events_jsonl]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + if args.record_event: + cmd.extend(["--record-event", args.record_event]) + cmd.extend(["--activation-type", args.activation_type]) + cmd.extend(["--outcome", args.outcome]) + cmd.extend(["--failure-type", args.failure_type]) + cmd.extend(["--source", args.source]) + cmd.extend(["--command", args.telemetry_command]) + if args.timestamp: + cmd.extend(["--timestamp", args.timestamp]) + if args.skill_name: + cmd.extend(["--skill-name", args.skill_name]) + if args.version: + cmd.extend(["--version", args.version]) + result = run_script("render_adoption_drift_report.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_telemetry_import(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir, "--input-jsonl", args.input_jsonl] + if args.events_jsonl: + cmd.extend(["--events-jsonl", args.events_jsonl]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + if args.source: + cmd.extend(["--source", args.source]) + if args.telemetry_command: + cmd.extend(["--command", args.telemetry_command]) + if args.dry_run: + cmd.append("--dry-run") + result = run_script("import_telemetry_events.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_telemetry_emit(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [ + skill_dir, + "--event", + args.event, + "--activation-type", + args.activation_type, + "--outcome", + args.outcome, + "--failure-type", + args.failure_type, + "--source", + args.source, + "--command", + args.telemetry_command, + ] + if args.output_jsonl: + cmd.extend(["--output-jsonl", args.output_jsonl]) + if args.timestamp: + cmd.extend(["--timestamp", args.timestamp]) + if args.skill_name: + cmd.extend(["--skill-name", args.skill_name]) + if args.version: + cmd.extend(["--version", args.version]) + if args.dry_run: + cmd.append("--dry-run") + result = run_script("emit_telemetry_event.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_telemetry_hooks(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.output_jsonl: + cmd.extend(["--output-jsonl", args.output_jsonl]) + result = run_script("render_telemetry_hook_recipes.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_review_waivers(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.waivers_json: + cmd.extend(["--waivers-json", args.waivers_json]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + if args.add_waiver: + required = { + "--gate-key": args.gate_key, + "--reviewer": args.reviewer, + "--reason": args.reason, + "--expires-at": args.expires_at, + } + missing = [name for name, value in required.items() if not value] + if missing: + print(json.dumps({"ok": False, "failures": [f"Missing required fields for --add-waiver: {', '.join(missing)}"]}, ensure_ascii=False, indent=2)) + return 2 + cmd.append("--add-waiver") + cmd.extend(["--gate-key", args.gate_key]) + cmd.extend(["--decision", args.decision]) + cmd.extend(["--reviewer", args.reviewer]) + cmd.extend(["--reason", args.reason]) + cmd.extend(["--expires-at", args.expires_at]) + if args.created_at: + cmd.extend(["--created-at", args.created_at]) + if args.evidence: + cmd.extend(["--evidence", args.evidence]) + if args.scope: + cmd.extend(["--scope", args.scope]) + result = run_script("render_review_waivers.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_review_annotations(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.annotations_json: + cmd.extend(["--annotations-json", args.annotations_json]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.write_template: + cmd.append("--write-template") + if args.add_annotation: + cmd.append("--add-annotation") + if args.annotation_id: + cmd.extend(["--annotation-id", args.annotation_id]) + if args.gate_key: + cmd.extend(["--gate-key", args.gate_key]) + if args.target_path: + cmd.extend(["--target-path", args.target_path]) + if args.line is not None: + cmd.extend(["--line", str(args.line)]) + if args.severity: + cmd.extend(["--severity", args.severity]) + if args.status: + cmd.extend(["--status", args.status]) + if args.reviewer: + cmd.extend(["--reviewer", args.reviewer]) + if args.created_at: + cmd.extend(["--created-at", args.created_at]) + if args.body: + cmd.extend(["--body", args.body]) + if args.suggested_action: + cmd.extend(["--suggested-action", args.suggested_action]) + if args.evidence: + cmd.extend(["--evidence", args.evidence]) + result = run_script("render_review_annotations.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_baseline_compare(args: argparse.Namespace) -> int: + result = run_script("render_baseline_compare.py", baseline_compare_args()) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_review(args: argparse.Namespace) -> int: + target_name = resolve_promotion_target(args.target) + bundle_dir = ROOT / "reports" / "iteration_bundles" / target_name + report = { + "ok": (bundle_dir / "bundle.json").exists() and (bundle_dir / "review.md").exists(), + "target": target_name, + "artifacts": { + "bundle_json": str((bundle_dir / "bundle.json").relative_to(ROOT)), + "bundle_md": str((bundle_dir / "bundle.md").relative_to(ROOT)), + "review_md": str((bundle_dir / "review.md").relative_to(ROOT)), + }, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 + + +def command_release_snapshot(args: argparse.Namespace) -> int: + target_name = resolve_promotion_target(args.target) + result = run_script( + "create_iteration_snapshot.py", + [ + "--target", + target_name, + "--label", + args.label, + ], + ) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_workspace_flow(args: argparse.Namespace) -> int: + selected_targets = ( + ["root", "team-frontend-review", "governed-incident-command"] + if args.target == "all" + else [args.target] + ) + steps = [] + snapshot_artifacts = [] + + for target in selected_targets: + steps.append( + { + "phase": "optimize-description", + "target": target, + "result": run_script("optimize_description.py", optimize_args_for_target(target, True)), + } + ) + + steps.extend( + [ + {"phase": "route-scorecard", "result": run_script("build_confusion_matrix.py", [])}, + {"phase": "promotion-check", "result": run_script("promotion_checker.py", [])}, + {"phase": "report-refresh", "result": run_script("render_eval_dashboard.py", [])}, + {"phase": "report-refresh", "result": run_script("render_description_drift_history.py", [])}, + {"phase": "report-refresh", "result": run_script("render_iteration_ledger.py", [])}, + {"phase": "report-refresh", "result": run_script("render_baseline_compare.py", baseline_compare_args())}, + {"phase": "report-refresh", "result": run_script("render_regression_history.py", [])}, + {"phase": "report-refresh", "result": run_script("render_context_reports.py", [])}, + {"phase": "report-refresh", "result": run_script("render_portability_report.py", [])}, + {"phase": "report-refresh", "result": run_script("python_compat_check.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_architecture_maintainability.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("compile_skill.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_adoption_drift_if_source_exists()}, + {"phase": "report-refresh", "result": run_script("render_telemetry_hook_recipes.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_review_waivers.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_review_annotations.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_world_class_evidence_plan.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_world_class_evidence_ledger.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_world_class_evidence_intake.py", [str(ROOT)])}, + { + "phase": "report-refresh", + "result": allow_report_status(run_script("render_world_class_submission_review.py", [str(ROOT)])), + }, + {"phase": "report-refresh", "result": run_script("render_world_class_operator_runbook.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_world_class_claim_guard.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_skill_os2_coverage.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_benchmark_reproducibility.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_skill_overview.py", [str(ROOT)])}, + {"phase": "report-refresh", "result": run_script("render_review_viewer.py", [str(ROOT)])}, + ] + ) + + for target in selected_targets: + review_target = resolve_promotion_target(target) + review_info = { + "bundle_json": f"reports/iteration_bundles/{review_target}/bundle.json", + "bundle_md": f"reports/iteration_bundles/{review_target}/bundle.md", + "review_md": f"reports/iteration_bundles/{review_target}/review.md", + } + snapshot = run_script( + "create_iteration_snapshot.py", + [ + "--target", + review_target, + "--label", + args.label, + ], + ) + snapshot_artifacts.append( + { + "target": review_target, + "review": review_info, + "snapshot": snapshot["payload"] if snapshot["payload"] is not None else snapshot, + } + ) + steps.append({"phase": "release-snapshot", "target": review_target, "result": snapshot}) + + report = { + "ok": all(step["result"]["ok"] for step in steps), + "target": args.target, + "label": args.label, + "steps": [ + { + "phase": step["phase"], + **({"target": step["target"]} if "target" in step else {}), + "command": step["result"]["command"], + "ok": step["result"]["ok"], + "returncode": step["result"]["returncode"], + } + for step in steps + ], + "artifacts": snapshot_artifacts, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 + + +def command_test(args: argparse.Namespace) -> int: + proc = subprocess.run( + ["make", args.target], + cwd=ROOT, + capture_output=True, + text=True, + ) + report = { + "ok": proc.returncode == 0, + "target": args.target, + "returncode": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 + + +def command_check_update(args: argparse.Namespace) -> int: + cmd = [] + if args.force: + cmd.append("--force") + if args.no_cache: + cmd.append("--no-cache") + if args.version_url: + cmd.extend(["--version-url", args.version_url]) + if args.manifest_url: + cmd.extend(["--manifest-url", args.manifest_url]) + if args.timeout is not None: + cmd.extend(["--timeout", str(args.timeout)]) + if args.allow_custom_update_url: + cmd.append("--allow-custom-update-url") + result = run_script("check_update.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def build_parser() -> argparse.ArgumentParser: + parser = build_cli_parser({name: value for name, value in globals().items() if name.startswith("command_")}) + add_telemetry_args(parser) + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + returncode = 2 + try: + returncode = args.func(args) + finally: + maybe_record_cli_event(ROOT, args, returncode) + raise SystemExit(returncode) + + +if __name__ == "__main__": + main() diff --git a/scripts/yao_cli_adaptation_commands.py b/scripts/yao_cli_adaptation_commands.py new file mode 100644 index 0000000..184e553 --- /dev/null +++ b/scripts/yao_cli_adaptation_commands.py @@ -0,0 +1,121 @@ +"""Adaptive scan, proposal, and apply command handlers for the Yao CLI.""" + +import argparse +import json +from pathlib import Path + +from yao_cli_runtime import run_script + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py to keep adaptive scan/proposal/apply command handlers outside the thin CLI orchestrator." + + +def command_adapt_scan(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir, "--source", args.source] + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.min_support is not None: + cmd.extend(["--min-support", str(args.min_support)]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + if args.allow_history_source: + cmd.append("--allow-history-source") + result = run_script("summarize_user_signals.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_adapt_propose(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.patterns_json: + cmd.extend(["--patterns-json", args.patterns_json]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + result = run_script("propose_adaptation.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_adapt_apply(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.proposal_id: + cmd.extend(["--proposal-id", args.proposal_id]) + if args.patch_file: + cmd.extend(["--patch-file", args.patch_file]) + if args.proposals_json: + cmd.extend(["--proposals-json", args.proposals_json]) + if args.approval_ledger: + cmd.extend(["--approval-ledger", args.approval_ledger]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + if args.today: + cmd.extend(["--today", args.today]) + if args.write_template: + cmd.append("--write-template") + if args.prepare_approval: + cmd.append("--prepare-approval") + if args.apply: + cmd.append("--apply") + if args.run_verification: + cmd.append("--run-verification") + if not args.rollback_on_failure: + cmd.append("--no-rollback-on-failure") + result = run_script("apply_adaptation.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_daily_skillops(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + if args.source: + cmd.extend(["--source", args.source]) + if args.patterns_json: + cmd.extend(["--patterns-json", args.patterns_json]) + if args.proposals_json: + cmd.extend(["--proposals-json", args.proposals_json]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.min_support is not None: + cmd.extend(["--min-support", str(args.min_support)]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + if args.allow_history_source: + cmd.append("--allow-history-source") + if args.no_refresh_source_reports: + cmd.append("--no-refresh-source-reports") + result = run_script("render_daily_skillops_report.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_weekly_curator(args: argparse.Namespace) -> int: + skill_dir = str(Path(args.skill_dir).resolve()) + cmd = [skill_dir] + for path in args.daily_json: + cmd.extend(["--daily-json", path]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + result = run_script("render_weekly_curator_report.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 diff --git a/scripts/yao_cli_config.py b/scripts/yao_cli_config.py new file mode 100644 index 0000000..e3383ca --- /dev/null +++ b/scripts/yao_cli_config.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +"""Pure configuration and shaping helpers for the Yao CLI.""" + +import json +from pathlib import Path + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py for CLI target maps and side-effect-free shaping helpers." + + +ROOT = Path(__file__).resolve().parent.parent + +TARGETS = { + "root": { + "description_file": ROOT / "SKILL.md", + "baseline_description_file": ROOT / "evals" / "baseline_description.txt", + "semantic_config": ROOT / "evals" / "semantic_config.json", + "dev_cases": ROOT / "evals" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "evals" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "evals" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "evals" / "adversarial" / "trigger_cases.json", + "output_json": ROOT / "reports" / "description_optimization.json", + "output_md": ROOT / "reports" / "description_optimization.md", + "title": "Root Description Optimization", + }, + "team-frontend-review": { + "description_file": ROOT / "examples" / "team-frontend-review" / "generated-skill" / "SKILL.md", + "baseline_description_file": ROOT / "examples" / "team-frontend-review" / "optimization" / "baseline_description.txt", + "semantic_config": ROOT / "examples" / "team-frontend-review" / "optimization" / "semantic_config.json", + "dev_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "adversarial" / "trigger_cases.json", + "output_json": ROOT / "examples" / "team-frontend-review" / "optimization" / "reports" / "description_optimization.json", + "output_md": ROOT / "examples" / "team-frontend-review" / "optimization" / "reports" / "description_optimization.md", + "title": "Frontend Review Description Optimization", + }, + "governed-incident-command": { + "description_file": ROOT / "examples" / "governed-incident-command" / "generated-skill" / "SKILL.md", + "baseline_description_file": ROOT / "examples" / "governed-incident-command" / "optimization" / "baseline_description.txt", + "semantic_config": ROOT / "examples" / "governed-incident-command" / "optimization" / "semantic_config.json", + "dev_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "adversarial" / "trigger_cases.json", + "output_json": ROOT / "examples" / "governed-incident-command" / "optimization" / "reports" / "description_optimization.json", + "output_md": ROOT / "examples" / "governed-incident-command" / "optimization" / "reports" / "description_optimization.md", + "title": "Governed Incident Description Optimization", + }, +} + +PROMOTION_TARGETS = { + "root": "yao-meta-skill", + "team-frontend-review": "team-frontend-review", + "governed-incident-command": "governed-incident-command", +} + +ARCHETYPE_MODE = { + "scaffold": "scaffold", + "production": "production", + "library": "library", + "governed": "governed", +} + + +def local_output_runner_command() -> str: + return json.dumps(["python3", "scripts/local_output_eval_runner.py"]) + + +def provider_output_runner_command( + provider: str, + model: str | None = None, + base_url: str | None = None, + api_format: str | None = None, + thinking: str | None = None, + temperature: float | None = None, + api_key_env: str | None = None, + allow_insecure_localhost: bool = False, + allow_custom_base_url: bool = False, +) -> str: + command = ["python3", "scripts/provider_output_eval_runner.py", "--provider", provider] + if provider == "deepseek": + api_format = api_format or "chat-completions" + thinking = thinking or "disabled" + api_key_env = api_key_env or "DEEPSEEK_API_KEY" + if model: + command.extend(["--model", model]) + if base_url: + command.extend(["--base-url", base_url]) + if api_format: + command.extend(["--api-format", api_format]) + if thinking: + command.extend(["--thinking", thinking]) + if temperature is not None: + command.extend(["--temperature", str(temperature)]) + if api_key_env: + command.extend(["--api-key-env", api_key_env]) + if allow_insecure_localhost: + command.append("--allow-insecure-localhost") + if allow_custom_base_url: + command.append("--allow-custom-base-url") + return json.dumps(command) + + +def resolve_target(name: str) -> dict: + if name not in TARGETS: + raise KeyError(f"Unknown target: {name}") + return TARGETS[name] + + +def resolve_promotion_target(name: str) -> str: + if name not in PROMOTION_TARGETS: + raise KeyError(f"Unknown promotion target: {name}") + return PROMOTION_TARGETS[name] + + +def baseline_compare_args() -> list[str]: + args = [] + for label, target in TARGETS.items(): + args.extend(["--entry", f"{label}::{target['output_json']}"]) + args.extend( + [ + "--output-json", + str(ROOT / "reports" / "baseline-compare.json"), + "--output-md", + str(ROOT / "reports" / "baseline-compare.md"), + ] + ) + return args + + +def infer_archetype(job: str, description: str) -> tuple[str, str]: + text = f"{job} {description}".lower() + if any(token in text for token in ("incident", "compliance", "security", "release", "govern", "audit", "policy")): + return "governed", "The request looks operationally sensitive, so governed is the safest default." + if any(token in text for token in ("shared", "cross-team", "library", "portable", "platform", "reusable across")): + return "library", "The request signals multi-team reuse or portability, so library is the better fit." + if any(token in text for token in ("review", "checklist", "team", "workflow", "process", "standardize")): + return "production", "The request looks team-reused and repeatable, so production fits better than scaffold." + return "scaffold", "The request still looks exploratory or lightweight, so scaffold keeps the first package lean." + + +def archetype_guidance(archetype: str) -> dict: + mapping = { + "scaffold": { + "first_gate": "trigger and exclusions", + "focus": "keep the first package small and avoid governance overhead", + }, + "production": { + "first_gate": "trigger plus one execution or eval asset", + "focus": "make the package reliable for team reuse", + }, + "library": { + "first_gate": "trigger, portability, and packaging semantics", + "focus": "treat the package as a shared capability with visible evidence", + }, + "governed": { + "first_gate": "trigger, governance, and review cadence", + "focus": "treat the package as a high-trust asset from the start", + }, + } + return mapping.get(archetype, mapping["scaffold"]) + + +def discovery_summary(job: str, primary_output: str, archetype: str, guidance: dict) -> str: + return ( + "\nHere's the shape I'm hearing so far:\n" + f"- Repeated job: {job}\n" + f"- Desired hand-back: {primary_output}\n" + f"- Best starting archetype: {archetype}\n" + f"- First gate: {guidance['first_gate']}\n" + f"- Current focus: {guidance['focus']}\n" + ) + + +def explicit_skill_request(job: str, description: str) -> bool: + text = f"{job} {description}".lower() + return any(token in text for token in ("skill", "workflow", "checklist", "package", "automate", "standardize")) + + +def diagnose_skill_candidates(job: str, primary_output: str, archetype: str, confidence: dict) -> dict: + fuzzy = not explicit_skill_request(job, primary_output) or confidence.get("score", 0) < 75 + candidates = [ + { + "shape": archetype, + "recommendation": "recommended", + "why_it_fits": "This is the lightest shape that matches the current recurring job signal.", + "limitation": "It should not deepen until the concrete output and exclusion boundary are clear.", + "first_pass": "Create one routeable skill with honest boundaries, one review report, and one next-step direction.", + } + ] + if archetype != "scaffold": + candidates.append( + { + "shape": "scaffold", + "recommendation": "fallback", + "why_it_fits": "Use this if the idea is still exploratory or personal.", + "limitation": "It may under-serve team reuse, portability, or governance needs.", + "first_pass": "Ship only SKILL.md, interface metadata, intent confidence, and review viewer.", + } + ) + if archetype not in {"production", "governed"}: + candidates.append( + { + "shape": "production", + "recommendation": "upgrade path", + "why_it_fits": "Use this when the workflow will be repeated by a team or needs consistent outputs.", + "limitation": "It adds validation and review cost that a personal scaffold may not need.", + "first_pass": "Add one practical eval or execution check after the trigger boundary is stable.", + } + ) + if archetype != "governed" and any(token in f"{job} {primary_output}".lower() for token in ("risk", "audit", "release", "policy", "security", "compliance")): + candidates.append( + { + "shape": "governed", + "recommendation": "risk path", + "why_it_fits": "Use this if the skill affects operational, compliance, security, or release decisions.", + "limitation": "It is too heavy unless ownership and review cadence are real.", + "first_pass": "Add owner, review cadence, lifecycle metadata, and reviewer-visible evidence.", + } + ) + return { + "mode": "fuzzy-problem-diagnosis" if fuzzy else "direct-skill-shaping", + "fuzzy": fuzzy, + "candidates": candidates[:3], + } + + +def diagnosis_note(diagnosis: dict) -> str: + lines = ["\nProblem-to-skill diagnosis:"] + for candidate in diagnosis["candidates"]: + lines.append( + f"- {candidate['shape']} ({candidate['recommendation']}): {candidate['why_it_fits']} " + f"First pass: {candidate['first_pass']}" + ) + return "\n".join(lines) + "\n" + + +def reference_visibility(reference_synthesis: dict) -> dict: + synthesis = reference_synthesis.get("synthesis", {}) if isinstance(reference_synthesis, dict) else {} + visibility = synthesis.get("visibility", {}) if isinstance(synthesis, dict) else {} + reasons = list(visibility.get("reasons", [])) + mode = visibility.get("mode", "explicit" if reasons else "silent") + return { + "mode": mode, + "user_decision_required": mode == "explicit", + "reasons": reasons, + "conflicts": synthesis.get("conflicts", []), + } + + +def recommendation_from_synthesis(reference_synthesis: dict, visibility: dict) -> dict: + synthesis = reference_synthesis.get("synthesis", {}) if isinstance(reference_synthesis, dict) else {} + recommendation = synthesis.get("recommendation", {}) if isinstance(synthesis, dict) else {} + borrow_now = recommendation.get("borrow_now") or synthesis.get("borrow_now", []) + avoid_now = recommendation.get("avoid_for_now") or synthesis.get("avoid_now", []) + summary = recommendation.get("summary") or ( + f"Start with {borrow_now[0]} Avoid {avoid_now[0]} for the first pass." + if borrow_now and avoid_now + else "Start with the smallest high-confidence pattern and keep the first pass light." + ) + why = recommendation.get("why") or "This recommendation comes from the benchmark synthesis and current intent confidence." + return { + "summary": summary, + "borrow_now": borrow_now[:2], + "avoid_for_now": avoid_now[:2], + "why": why, + "user_decision_required": visibility["user_decision_required"], + } diff --git a/scripts/yao_cli_create_commands.py b/scripts/yao_cli_create_commands.py new file mode 100644 index 0000000..265e6ed --- /dev/null +++ b/scripts/yao_cli_create_commands.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +"""Creation command handlers for the Yao CLI.""" + +import argparse +import json +import sys + +from github_benchmark_scan import build_query +from render_intent_confidence import assess_intent_confidence +from yao_cli_config import ( + ARCHETYPE_MODE, + archetype_guidance, + diagnose_skill_candidates, + diagnosis_note, + discovery_summary, + infer_archetype, + recommendation_from_synthesis, + reference_visibility, +) +from yao_cli_runtime import run_script + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py to keep skill creation and quickstart command handlers out of the CLI orchestrator." + + +def prompt_with_default(label: str, default: str) -> str: + sys.stderr.write(f"{label} [{default}]: ") + sys.stderr.flush() + value = sys.stdin.readline().strip() + return value or default + + +def prompt_optional(label: str, default: str = "skip") -> str: + sys.stderr.write(f"{label} [{default}]: ") + sys.stderr.flush() + value = sys.stdin.readline().strip() + return value or default + + +def prompt_optional_entries(label: str) -> list[str]: + sys.stderr.write(f"{label} [none]: ") + sys.stderr.flush() + value = sys.stdin.readline().strip() + if not value or value.lower() in {"none", "no", "n"}: + return [] + return [item.strip() for item in value.split(",") if item.strip()] + + +def update_context_slot(context: dict, slot: str, answer: str, list_mode: bool) -> None: + value = answer.strip() + if not value or value.lower() in {"skip", "none", "no", "n"}: + return + if list_mode: + context[slot] = [item.strip() for item in value.split(",") if item.strip()] + else: + context[slot] = value + + +def intent_confidence_note(summary: dict) -> str: + lines = [ + f"\nIntent confidence: {summary['score']}/100 ({summary['band']}).", + f"- Recommended action: {summary['recommended_action']}", + ] + if summary.get("gaps"): + top_gap = summary["gaps"][0] + lines.append(f"- Biggest gap: {top_gap['label']} — {top_gap['reason']}") + return "\n".join(lines) + "\n" + + +def maybe_emit_update_notice(args: argparse.Namespace) -> None: + if getattr(args, "no_update_check", False): + return + result = run_script("check_update.py", []) + payload = result["payload"] if result["payload"] is not None else {} + if not result["ok"] and not payload: + return + if payload.get("update_available"): + sys.stderr.write( + "\nUpdate available for yao-meta-skill: " + f"{payload.get('local_version')} -> {payload.get('remote_version')}.\n" + f"Run: {payload.get('install_hint')}\n" + ) + + +def command_init(args: argparse.Namespace) -> int: + cmd = [ + args.name, + "--description", + args.description, + "--output-dir", + args.output_dir, + "--mode", + args.mode, + "--archetype", + args.archetype, + *(["--title", args.title] if args.title else []), + ] + for reference in args.external_reference: + cmd.extend(["--external-reference", reference]) + for reference in args.user_reference: + cmd.extend(["--user-reference", reference]) + for constraint in args.local_constraint: + cmd.extend(["--local-constraint", constraint]) + if args.github_query: + cmd.extend(["--github-query", args.github_query]) + cmd.extend(["--github-top-n", str(args.github_top_n)]) + if args.github_fixture_dir: + cmd.extend(["--github-fixture-dir", args.github_fixture_dir]) + if args.intent_job: + cmd.extend(["--intent-job", args.intent_job]) + for item in args.intent_real_input: + cmd.extend(["--intent-real-input", item]) + if args.intent_primary_output: + cmd.extend(["--intent-primary-output", args.intent_primary_output]) + for item in args.intent_exclusion: + cmd.extend(["--intent-exclusion", item]) + for item in args.intent_constraint: + cmd.extend(["--intent-constraint", item]) + for item in args.intent_standard: + cmd.extend(["--intent-standard", item]) + if args.intent_correction: + cmd.extend(["--intent-correction", args.intent_correction]) + result = run_script("init_skill.py", cmd) + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_quickstart(args: argparse.Namespace) -> int: + maybe_emit_update_notice(args) + sys.stderr.write("Let's start gently. You do not need a polished brief here.\n") + sys.stderr.write("Give me the real work in your own words, and I will help turn it into a clean first-pass skill.\n") + sys.stderr.write("While we shape the first pass, I will quietly check a few strong public patterns in the background and only surface them if there is real uncertainty or a design conflict.\n") + name = args.name or prompt_with_default("Skill name", "my-skill") + job = args.job or prompt_with_default( + "In your own words, what repeated work do you most want this skill to reliably handle", + "Turn a repeated workflow into a reusable skill.", + ) + real_inputs = args.real_input or prompt_optional_entries( + "What material will people actually hand to this skill in practice (comma-separated)" + ) + primary_output = args.primary_output or prompt_with_default( + "If it works beautifully, what should it hand back so you or the next person can keep moving", + "A reusable skill package.", + ) + description = args.description or f"{job.rstrip('.')} Primary output: {primary_output.rstrip('.')}." + intent_context = { + "job": job, + "real_inputs": real_inputs, + "primary_output": primary_output, + "description": description, + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [], + } + inferred_archetype, archetype_reason = infer_archetype(job, description) + guidance = archetype_guidance(inferred_archetype) + sys.stderr.write(discovery_summary(job, primary_output, inferred_archetype, guidance)) + correction = prompt_optional( + "If I am off, what is the first thing I should correct before I package this idea", + "looks right", + ) + if correction.lower() not in {"looks right", "skip", "none", "no"}: + description = f"{description.rstrip('.')} Keep this correction in scope: {correction.rstrip('.')}." + intent_context["description"] = description + intent_context["correction"] = correction + inferred_archetype, archetype_reason = infer_archetype(job, description) + guidance = archetype_guidance(inferred_archetype) + sys.stderr.write("\nThanks. I tightened the frame before moving on.\n") + sys.stderr.write(discovery_summary(job, primary_output, inferred_archetype, guidance)) + confidence = assess_intent_confidence(intent_context) + sys.stderr.write(intent_confidence_note(confidence)) + diagnosis = diagnose_skill_candidates(job, primary_output, inferred_archetype, confidence) + if diagnosis["fuzzy"]: + sys.stderr.write(diagnosis_note(diagnosis)) + if not confidence["gate_passed"]: + sys.stderr.write("Before I package this idea, I want to close the highest-leverage gaps instead of guessing.\n") + for follow_up in confidence.get("follow_up_questions", [])[:2]: + answer = prompt_optional(follow_up["question"], "skip") + update_context_slot(intent_context, follow_up["slot"], answer, follow_up["list"]) + confidence = assess_intent_confidence(intent_context) + sys.stderr.write("\nI tightened the intent frame once more before moving on.\n") + sys.stderr.write(intent_confidence_note(confidence)) + diagnosis = diagnose_skill_candidates(job, primary_output, inferred_archetype, confidence) + if diagnosis["fuzzy"]: + sys.stderr.write(diagnosis_note(diagnosis)) + archetype = args.archetype or prompt_with_default("I would start with this archetype (scaffold/production/library/governed)", inferred_archetype) + archetype = archetype if archetype in ARCHETYPE_MODE else inferred_archetype + default_mode = ARCHETYPE_MODE[archetype] + mode = args.mode or prompt_with_default("For the first pass, I would keep the mode here (scaffold/production/library/governed)", default_mode) + mode = mode if mode in ARCHETYPE_MODE.values() else default_mode + diagnosis = diagnose_skill_candidates(job, primary_output, archetype, confidence) + guidance = archetype_guidance(archetype) + sys.stderr.write( + f"\nGood. I will treat this as `{archetype}` in `{mode}` mode, so the first pass stays focused on {guidance['focus']}.\n" + ) + user_references = args.user_reference or prompt_optional_entries( + "If there is anything you admire and want me to learn from as pattern hints, send it here (repo, product, page, workflow; comma-separated)" + ) + external_references = args.external_reference or [] + prompted_constraints = args.constraint if getattr(args, "constraint", None) else ([] if args.local_constraint else prompt_optional_entries( + "Tell me any local constraints I must keep in view (privacy, naming, compatibility; comma-separated)" + )) + local_constraints = args.local_constraint or prompted_constraints or intent_context.get("constraints", []) + intent_context["user_references"] = user_references + intent_context["constraints"] = local_constraints + confidence = assess_intent_confidence(intent_context) + github_query = args.github_query or build_query(" ".join(filter(None, [job, primary_output, description]))) + title = args.title or name.replace("-", " ").title() + guidance = archetype_guidance(archetype) + cmd = [ + name, + "--description", + description, + "--title", + title, + "--output-dir", + args.output_dir, + "--mode", + mode, + "--archetype", + archetype, + "--github-query", + github_query, + "--github-top-n", + str(args.github_top_n), + "--intent-job", + job, + "--intent-primary-output", + primary_output, + ] + for item in real_inputs: + cmd.extend(["--intent-real-input", item]) + for item in intent_context.get("exclusions", []): + cmd.extend(["--intent-exclusion", item]) + for item in intent_context.get("constraints", []): + cmd.extend(["--intent-constraint", item]) + for item in intent_context.get("standards", []): + cmd.extend(["--intent-standard", item]) + if intent_context.get("correction"): + cmd.extend(["--intent-correction", intent_context["correction"]]) + if args.github_fixture_dir: + cmd.extend(["--github-fixture-dir", args.github_fixture_dir]) + for reference in external_references: + cmd.extend(["--external-reference", reference]) + for reference in user_references: + cmd.extend(["--user-reference", reference]) + for constraint in local_constraints: + cmd.extend(["--local-constraint", constraint]) + result = run_script("init_skill.py", cmd) + payload = result["payload"] if result["payload"] is not None else result + reference_synthesis = payload.get("reference_synthesis") or {} + visibility = reference_visibility(reference_synthesis) + recommendation = recommendation_from_synthesis(reference_synthesis, visibility) + sys.stderr.write(f"\nRecommendation: {recommendation['summary']}\n") + if visibility["user_decision_required"]: + if visibility["conflicts"]: + sys.stderr.write(f"I am surfacing this because there is a real design conflict: {visibility['conflicts'][0]['summary']}\n") + else: + sys.stderr.write("I am surfacing this because intent is still settling and the package should not deepen on guesswork.\n") + else: + sys.stderr.write("I will keep the underlying benchmark evidence in the reviewer reports and move ahead with this recommendation.\n") + if payload.get("report_view", {}).get("html_report"): + sys.stderr.write(f"Skill report: {payload['report_view']['html_report']}\n") + if payload.get("report_view", {}).get("interpretation_report"): + sys.stderr.write(f"Skill interpretation: {payload['report_view']['interpretation_report']}\n") + + next_steps = [ + "Open reports/skill-interpretation.html to review the generated Skill interpretation report.", + "Open reports/skill-overview.html to review the generated Skill audit report.", + "Open reports/intent-dialogue.md and tighten the real job, outputs, and exclusions.", + "Open reports/review-studio.html to inspect the Review Studio 2.0 gate view before release.", + "Open reports/review-viewer.html to explain the package to a first-time reviewer.", + "Use reports/iteration-directions.md to choose only one high-value next move before adding more files.", + ] + if visibility["user_decision_required"]: + next_steps.insert( + 1, + "Open reports/reference-synthesis.md if you want to inspect why the recommendation was surfaced and which tradeoff needs a call.", + ) + report = { + "ok": result["ok"], + "root": payload.get("root"), + "mode": mode, + "archetype": archetype, + "artifacts": payload.get("artifacts", {}), + "report_view": payload.get("report_view", {}), + "intent_confidence": { + "score": confidence["score"], + "band": confidence["band"], + "gate_passed": confidence["gate_passed"], + "recommended_action": confidence["recommended_action"], + }, + "recommendation": recommendation, + "reference_mode": { + "mode": visibility["mode"], + "user_decision_required": visibility["user_decision_required"], + }, + "reviewer_evidence": { + "visibility": "full evidence in reports and review-viewer", + "artifacts": { + "benchmark_scan": payload.get("artifacts", {}).get("github_benchmark_scan_md"), + "reference_synthesis": payload.get("artifacts", {}).get("reference_synthesis_md"), + "artifact_design_profile": payload.get("artifacts", {}).get("artifact_design_profile_md"), + "prompt_quality_profile": payload.get("artifacts", {}).get("prompt_quality_profile_md"), + "system_model": payload.get("artifacts", {}).get("system_model_md"), + "skill_interpretation": payload.get("artifacts", {}).get("skill_interpretation_html"), + "review_studio": payload.get("artifacts", {}).get("review_studio_html"), + "review_viewer": payload.get("artifacts", {}).get("review_viewer_html"), + }, + }, + "guidance": { + "archetype_reason": archetype_reason, + "problem_diagnosis": diagnosis, + "why_this_mode": ( + "Scaffold mode keeps the first package light and lets you postpone governance-heavy work until reuse becomes real." + if mode == "scaffold" + else "This mode expects stronger lifecycle metadata, validation, and review discipline." + ), + "first_gate": guidance["first_gate"], + "focus": guidance["focus"], + "next_steps": next_steps, + "experience_note": ( + "The first pass should feel more like guided co-creation than a worksheet. " + "The system should make benchmark and pattern calls quietly unless there is a real reason to ask you to choose." + ), + }, + } + if visibility["user_decision_required"]: + report["uncertainty_or_conflict"] = { + "reasons": visibility["reasons"], + "conflicts": visibility["conflicts"], + "note": "A design decision still needs your input before the package should be deepened.", + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 diff --git a/scripts/yao_cli_distribution_commands.py b/scripts/yao_cli_distribution_commands.py new file mode 100644 index 0000000..95856e9 --- /dev/null +++ b/scripts/yao_cli_distribution_commands.py @@ -0,0 +1,164 @@ +"""Distribution, packaging, and runtime-gate command handlers for the Yao CLI.""" + +import argparse +import json +from pathlib import Path + +from yao_cli_runtime import run_script + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py to keep distribution and runtime gate handlers outside the thin CLI orchestrator." + + +def emit_result(result: dict) -> int: + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_skill_ir(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.validate_only: + cmd.append("--validate-only") + return emit_result(run_script("export_skill_ir.py", cmd)) + + +def command_compile_skill(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + for target in args.target or []: + cmd.extend(["--target", target]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + return emit_result(run_script("compile_skill.py", cmd)) + + +def command_conformance(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + for target in args.target or []: + cmd.extend(["--target", target]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + return emit_result(run_script("run_conformance_suite.py", cmd)) + + +def command_runtime_permissions(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + if args.package_dir: + cmd.extend(["--package-dir", args.package_dir]) + for target in args.target or []: + cmd.extend(["--target", target]) + if getattr(args, "install_simulation_json", None): + cmd.extend(["--install-simulation-json", args.install_simulation_json]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + return emit_result(run_script("probe_runtime_permissions.py", cmd)) + + +def command_trust(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + return emit_result(run_script("trust_check.py", cmd)) + + +def command_skill_atlas(args: argparse.Namespace) -> int: + cmd = ["--workspace-root", str(Path(args.workspace_root).resolve())] + if args.output_dir: + cmd.extend(["--output-dir", args.output_dir]) + if args.report_html: + cmd.extend(["--report-html", args.report_html]) + if args.report_json: + cmd.extend(["--report-json", args.report_json]) + if args.overlap_threshold is not None: + cmd.extend(["--overlap-threshold", str(args.overlap_threshold)]) + if args.today: + cmd.extend(["--today", args.today]) + return emit_result(run_script("build_skill_atlas.py", cmd)) + + +def command_registry_audit(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + if args.registry_dir: + cmd.extend(["--registry-dir", args.registry_dir]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + return emit_result(run_script("registry_audit.py", cmd)) + + +def command_package_verify(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + if args.package_dir: + cmd.extend(["--package-dir", args.package_dir]) + if args.expectations: + cmd.extend(["--expectations", args.expectations]) + if args.registry_json: + cmd.extend(["--registry-json", args.registry_json]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.require_zip: + cmd.append("--require-zip") + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + return emit_result(run_script("verify_package.py", cmd)) + + +def command_install_simulate(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + if args.package_dir: + cmd.extend(["--package-dir", args.package_dir]) + if args.install_root: + cmd.extend(["--install-root", args.install_root]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + return emit_result(run_script("simulate_install.py", cmd)) + + +def command_upgrade_check(args: argparse.Namespace) -> int: + cmd = [str(Path(args.skill_dir).resolve())] + cmd.extend(["--previous-package-json", args.previous_package_json]) + if args.current_package_json: + cmd.extend(["--current-package-json", args.current_package_json]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + return emit_result(run_script("upgrade_check.py", cmd)) + + +def command_package(args: argparse.Namespace) -> int: + cmd = [ + str(Path(args.skill_dir).resolve()), + "--output-dir", + args.output_dir, + ] + for platform in args.platform or ["generic"]: + cmd.extend(["--platform", platform]) + if args.expectations: + cmd.extend(["--expectations", args.expectations]) + if args.zip: + cmd.append("--zip") + return emit_result(run_script("cross_packager.py", cmd)) diff --git a/scripts/yao_cli_operator_commands.py b/scripts/yao_cli_operator_commands.py new file mode 100644 index 0000000..5dfe85c --- /dev/null +++ b/scripts/yao_cli_operator_commands.py @@ -0,0 +1,424 @@ +"""Operator UX commands for installation, docs sync, and PR review evidence.""" + +import argparse +import json +import shutil +import subprocess +from pathlib import Path +from typing import Any + +from yao_cli_runtime import ROOT + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py for operator-facing install, localized docs, and PR review diagnostics." + + +DEFAULT_DOC_PAIRS = [ + { + "key": "skill-os-2-upgrade", + "source": "## Skill OS 2.0 Upgrade", + "localized": "## Skill OS 2.0 升级", + }, + { + "key": "from-1-to-2", + "source": "## From 1.0 to 2.0", + "localized": "## 从 1.0 到 2.0", + }, + { + "key": "use-cases", + "source": "## 2.0 Use Cases", + "localized": "## 2.0 使用场景", + }, + { + "key": "operator-ux", + "source": "## Operator UX Commands", + "localized": "## Operator UX 命令", + }, + { + "key": "architecture", + "source": "## Architecture", + "localized": "## 架构图", + }, + { + "key": "benchmark", + "source": "## Weighted Quality Benchmark", + "localized": "## 加权质量评测", + }, + { + "key": "claim-boundary", + "source": 'Current posture: the repository is ready for beta and external testing', + "localized": "当前发布口径:仓库已经适合进入测试版和外部试用", + }, +] + + +def _expand(path: str) -> Path: + return Path(path).expanduser().resolve() + + +def _display(path: Path) -> str: + try: + return str(path.relative_to(Path.home())) + except ValueError: + return str(path) + + +def _skill_path_status(root: Path, skill_name: str, expected_source: Path) -> dict[str, Any]: + path = root / skill_name + exists = path.exists() + resolved = path.resolve() if exists or path.is_symlink() else path + has_skill_md = (path / "SKILL.md").exists() + return { + "root": str(root), + "path": str(path), + "exists": exists, + "is_symlink": path.is_symlink(), + "resolved": str(resolved), + "has_skill_md": has_skill_md, + "points_to_expected_source": resolved == expected_source, + } + + +def _recommend_install_status(codex: dict[str, Any], agents: dict[str, Any], disabled: dict[str, Any]) -> list[str]: + recommendations = [] + if codex["has_skill_md"]: + recommendations.append("Codex/Cortex can discover this skill through .codex/skills; restart the app if the list looks stale.") + if agents["has_skill_md"]: + recommendations.append("The skill is also active under .agents/skills; expect a duplicate entry when this repo is open.") + if not codex["has_skill_md"] and not agents["has_skill_md"] and disabled["has_skill_md"]: + recommendations.append("Only the disabled mirror exists; run make sync-active-install or add a .codex/skills symlink to activate it.") + if not codex["has_skill_md"] and not agents["has_skill_md"] and not disabled["has_skill_md"]: + recommendations.append("No active or disabled install was found for this skill name.") + if codex["has_skill_md"] and not codex["points_to_expected_source"]: + recommendations.append("The .codex/skills entry points somewhere else; inspect the symlink before editing or syncing.") + if not recommendations: + recommendations.append("No action required.") + return recommendations + + +def command_install_status(args: argparse.Namespace) -> int: + skill_name = args.skill_name + expected_source = _expand(args.expected_source) + codex_root = _expand(args.codex_root) + agents_root = _expand(args.agents_root) + disabled_root = _expand(args.disabled_root) + + codex = _skill_path_status(codex_root, skill_name, expected_source) + agents = _skill_path_status(agents_root, skill_name, expected_source) + disabled = _skill_path_status(disabled_root, skill_name, expected_source) + active_locations = [name for name, item in (("codex", codex), ("agents", agents)) if item["has_skill_md"]] + report = { + "ok": True, + "skill_name": skill_name, + "expected_source": str(expected_source), + "summary": { + "codex_active": codex["has_skill_md"], + "agents_active": agents["has_skill_md"], + "disabled_mirror": disabled["has_skill_md"], + "active_location_count": len(active_locations), + "duplicate_active": len(active_locations) > 1, + "active_locations": active_locations, + }, + "locations": { + "codex": codex, + "agents": agents, + "disabled": disabled, + }, + "recommendations": _recommend_install_status(codex, agents, disabled), + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 + + +def _load_pairs(raw_pairs: list[str]) -> list[dict[str, str]]: + if not raw_pairs: + return DEFAULT_DOC_PAIRS + pairs = [] + for raw in raw_pairs: + parts = raw.split("::") + if len(parts) != 3: + raise ValueError("--pair must use key::source-marker::localized-marker") + pairs.append({"key": parts[0], "source": parts[1], "localized": parts[2]}) + return pairs + + +def _write_json(path: str | None, payload: dict[str, Any]) -> None: + if not path: + return + target = _expand(path) + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + +def _write_docs_sync_md(path: str | None, payload: dict[str, Any]) -> None: + if not path: + return + target = _expand(path) + target.parent.mkdir(parents=True, exist_ok=True) + lines = [ + "# Localized Docs Sync Check", + "", + f"- ok: `{str(payload['ok']).lower()}`", + f"- source: `{payload['source']}`", + f"- localized: `{payload['localized']}`", + f"- checked: `{payload['summary']['checked_count']}`", + f"- missing: `{payload['summary']['missing_count']}`", + f"- skipped: `{payload['summary']['skipped_count']}`", + "", + "## Pairs", + "", + ] + for item in payload["pairs"]: + lines.append(f"- `{item['key']}`: `{item['status']}`") + target.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def command_localized_doc_sync_check(args: argparse.Namespace) -> int: + source_path = _expand(args.source) + localized_path = _expand(args.localized) + try: + pairs = _load_pairs(args.pair) + except ValueError as exc: + report = { + "ok": False, + "source": str(source_path), + "localized": str(localized_path), + "summary": {"checked_count": 0, "missing_count": 0, "skipped_count": 0}, + "pairs": [], + "missing": [], + "failures": [str(exc)], + } + _write_json(args.output_json, report) + _write_docs_sync_md(args.output_md, report) + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 2 + missing_paths = [str(path) for path in (source_path, localized_path) if not path.exists()] + if missing_paths: + report = { + "ok": False, + "source": str(source_path), + "localized": str(localized_path), + "summary": {"checked_count": 0, "missing_count": 0, "skipped_count": 0}, + "pairs": [], + "missing": [], + "failures": [f"Missing docs file: {path}" for path in missing_paths], + } + _write_json(args.output_json, report) + _write_docs_sync_md(args.output_md, report) + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 2 + source_text = source_path.read_text(encoding="utf-8") + localized_text = localized_path.read_text(encoding="utf-8") + results = [] + for pair in pairs: + source_present = pair["source"] in source_text + localized_present = pair["localized"] in localized_text + if not source_present: + status = "skipped-source-missing" + elif localized_present: + status = "pass" + else: + status = "missing-localized-marker" + results.append( + { + **pair, + "source_present": source_present, + "localized_present": localized_present, + "status": status, + } + ) + missing = [item for item in results if item["status"] == "missing-localized-marker"] + checked = [item for item in results if item["source_present"]] + report = { + "ok": not missing, + "source": str(source_path), + "localized": str(localized_path), + "summary": { + "checked_count": len(checked), + "missing_count": len(missing), + "skipped_count": len(results) - len(checked), + }, + "pairs": results, + "missing": missing, + } + _write_json(args.output_json, report) + _write_docs_sync_md(args.output_md, report) + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 + + +def _run_gh(args: list[str]) -> subprocess.CompletedProcess[str]: + return subprocess.run(["gh", *args], capture_output=True, text=True) + + +def _gh_available() -> bool: + return shutil.which("gh") is not None + + +def _review_depth(changed_files: int, changed_lines: int) -> str: + if changed_files <= 5 and changed_lines < 100: + return "quick" + if changed_files <= 10 and changed_lines < 500: + return "standard" + return "deep" + + +def _check_summary(checks: list[dict[str, Any]]) -> dict[str, Any]: + pending = [] + failed = [] + passed = [] + for item in checks: + name = str(item.get("name") or item.get("workflowName") or "check") + status = str(item.get("status") or "").upper() + conclusion = str(item.get("conclusion") or "").upper() + if status and status != "COMPLETED": + pending.append(name) + elif conclusion in {"SUCCESS", "SKIPPED", "NEUTRAL"}: + passed.append(name) + else: + failed.append(name) + return { + "present": bool(checks), + "passed_count": len(passed), + "pending_count": len(pending), + "failed_count": len(failed), + "passed": passed, + "pending": pending, + "failed": failed, + } + + +def _pr_decision(view: dict[str, Any], checks: dict[str, Any], require_checks: bool) -> str: + if view.get("state") != "OPEN": + return "not-open" + if view.get("isDraft"): + return "draft" + if view.get("mergeable") not in {"MERGEABLE", "UNKNOWN"}: + return "not-mergeable" + if checks["failed_count"]: + return "fix-failing-checks" + if checks["pending_count"]: + return "wait-for-checks" + if require_checks and not checks["present"]: + return "checks-required" + if not checks["present"]: + return "local-verification-required" + return "mergeable-after-review" + + +def _write_pr_review_md(path: str | None, payload: dict[str, Any]) -> None: + if not path: + return + target = _expand(path) + target.parent.mkdir(parents=True, exist_ok=True) + lines = [ + "# PR Review Report", + "", + f"- ok: `{str(payload['ok']).lower()}`", + f"- PR: `{payload['pr']}`", + f"- repo: `{payload.get('repo') or 'current'}`", + f"- decision: `{payload['decision']}`", + f"- review depth: `{payload['review_depth']}`", + f"- changed files: `{payload['summary']['changed_files']}`", + f"- additions: `{payload['summary']['additions']}`", + f"- deletions: `{payload['summary']['deletions']}`", + f"- checks present: `{str(payload['checks']['present']).lower()}`", + "", + "## Files", + "", + ] + for path_item in payload["files"]: + lines.append(f"- `{path_item}`") + lines.extend(["", "## Suggested Commands", ""]) + for command in payload["suggested_commands"]: + lines.append(f"- `{command}`") + target.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def command_pr_review_report(args: argparse.Namespace) -> int: + if not _gh_available(): + report = { + "ok": False, + "pr": args.pr, + "repo": args.repo, + "decision": "missing-gh-cli", + "failures": ["GitHub CLI `gh` is not available on PATH."], + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 2 + + fields = ",".join( + [ + "number", + "title", + "state", + "isDraft", + "mergeable", + "author", + "baseRefName", + "headRefName", + "url", + "additions", + "deletions", + "changedFiles", + "commits", + "statusCheckRollup", + "reviewDecision", + "maintainerCanModify", + ] + ) + view_args = ["pr", "view", args.pr, "--json", fields] + diff_args = ["pr", "diff", args.pr, "--name-only"] + if args.repo: + view_args.extend(["--repo", args.repo]) + diff_args.extend(["--repo", args.repo]) + view_proc = _run_gh(view_args) + if view_proc.returncode != 0: + report = { + "ok": False, + "pr": args.pr, + "repo": args.repo, + "decision": "gh-pr-view-failed", + "returncode": view_proc.returncode, + "stderr": view_proc.stderr.strip(), + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 2 + view = json.loads(view_proc.stdout) + diff_proc = _run_gh(diff_args) + files = [line.strip() for line in diff_proc.stdout.splitlines() if line.strip()] if diff_proc.returncode == 0 else [] + checks = _check_summary(view.get("statusCheckRollup") or []) + changed_lines = int(view.get("additions") or 0) + int(view.get("deletions") or 0) + depth = _review_depth(int(view.get("changedFiles") or len(files)), changed_lines) + decision = _pr_decision(view, checks, args.require_checks) + report = { + "ok": decision not in {"missing-gh-cli", "gh-pr-view-failed", "checks-required", "fix-failing-checks", "not-mergeable", "draft", "not-open"}, + "pr": str(view.get("number", args.pr)), + "repo": args.repo, + "url": view.get("url", ""), + "title": view.get("title", ""), + "state": view.get("state", ""), + "mergeable": view.get("mergeable", ""), + "decision": decision, + "review_depth": depth, + "summary": { + "changed_files": int(view.get("changedFiles") or len(files)), + "additions": int(view.get("additions") or 0), + "deletions": int(view.get("deletions") or 0), + "commit_count": len(view.get("commits") or []), + "maintainer_can_modify": bool(view.get("maintainerCanModify")), + }, + "checks": checks, + "files": files, + "suggested_commands": [ + f"gh pr view {args.pr}{' --repo ' + args.repo if args.repo else ''} --json number,title,state,mergeable,statusCheckRollup", + f"gh pr diff {args.pr}{' --repo ' + args.repo if args.repo else ''} --name-only", + f"git fetch origin pull/{args.pr}/head:refs/tmp/pr-{args.pr}", + f"git diff --check origin/main...refs/tmp/pr-{args.pr}", + ], + } + _write_json(args.output_json, report) + _write_pr_review_md(args.output_md, report) + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 diff --git a/scripts/yao_cli_output_commands.py b/scripts/yao_cli_output_commands.py new file mode 100644 index 0000000..170a3ae --- /dev/null +++ b/scripts/yao_cli_output_commands.py @@ -0,0 +1,134 @@ +"""Output evaluation and human-review command handlers for the Yao CLI.""" + +import argparse +import json + +from yao_cli_config import provider_output_runner_command +from yao_cli_runtime import run_script + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py to keep output evaluation and review handlers outside the thin CLI orchestrator." + + +def emit_result(result: dict) -> int: + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def command_output_eval(args: argparse.Namespace) -> int: + cmd = [] + if args.cases: + cmd.extend(["--cases", args.cases]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.blind_pack_json: + cmd.extend(["--blind-pack-json", args.blind_pack_json]) + if args.blind_pack_md: + cmd.extend(["--blind-pack-md", args.blind_pack_md]) + if args.blind_answer_key_json: + cmd.extend(["--blind-answer-key-json", args.blind_answer_key_json]) + return emit_result(run_script("run_output_eval.py", cmd)) + + +def command_output_execution(args: argparse.Namespace) -> int: + cmd = [] + if args.cases: + cmd.extend(["--cases", args.cases]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.runner_command and args.provider_runner: + payload = { + "schema_version": "1.0", + "ok": False, + "failures": ["Use either --runner-command or --provider-runner, not both."], + } + print(json.dumps(payload, ensure_ascii=False, indent=2)) + return 2 + if args.provider_runner: + cmd.extend( + [ + "--runner-command", + provider_output_runner_command( + args.provider_runner, + model=args.provider_model, + base_url=args.provider_base_url, + api_format=args.provider_api_format, + thinking=args.provider_thinking, + temperature=args.provider_temperature, + api_key_env=args.api_key_env, + allow_insecure_localhost=args.allow_insecure_localhost, + allow_custom_base_url=args.allow_custom_base_url, + ), + ] + ) + elif args.runner_command: + cmd.extend(["--runner-command", args.runner_command]) + if args.timeout_seconds is not None: + cmd.extend(["--timeout-seconds", str(args.timeout_seconds)]) + return emit_result(run_script("run_output_execution.py", cmd)) + + +def command_output_review_kit(args: argparse.Namespace) -> int: + cmd = [] + if args.blind_pack_json: + cmd.extend(["--blind-pack-json", args.blind_pack_json]) + if args.blind_pack_md: + cmd.extend(["--blind-pack-md", args.blind_pack_md]) + if args.decisions: + cmd.extend(["--decisions", args.decisions]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.output_html: + cmd.extend(["--output-html", args.output_html]) + if args.write_template: + cmd.append("--write-template") + return emit_result(run_script("prepare_output_review_kit.py", cmd)) + + +def command_output_review(args: argparse.Namespace) -> int: + cmd = [] + if args.blind_pack: + cmd.extend(["--blind-pack", args.blind_pack]) + if args.answer_key: + cmd.extend(["--answer-key", args.answer_key]) + if args.decisions: + cmd.extend(["--decisions", args.decisions]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.output_md: + cmd.extend(["--output-md", args.output_md]) + if args.write_template: + cmd.append("--write-template") + return emit_result(run_script("adjudicate_output_review.py", cmd)) + + +def command_output_review_import(args: argparse.Namespace) -> int: + cmd = ["--input", args.input] + if args.format: + cmd.extend(["--format", args.format]) + if args.blind_pack: + cmd.extend(["--blind-pack", args.blind_pack]) + if args.output_json: + cmd.extend(["--output-json", args.output_json]) + if args.reviewer: + cmd.extend(["--reviewer", args.reviewer]) + if args.reviewed_at: + cmd.extend(["--reviewed-at", args.reviewed_at]) + if args.blind_review_attested: + cmd.append("--blind-review-attested") + if args.run_adjudication: + cmd.append("--run-adjudication") + if args.answer_key: + cmd.extend(["--answer-key", args.answer_key]) + if args.adjudication_json: + cmd.extend(["--adjudication-json", args.adjudication_json]) + if args.adjudication_md: + cmd.extend(["--adjudication-md", args.adjudication_md]) + return emit_result(run_script("import_output_review_decisions.py", cmd)) diff --git a/scripts/yao_cli_parser.py b/scripts/yao_cli_parser.py new file mode 100644 index 0000000..dab6a01 --- /dev/null +++ b/scripts/yao_cli_parser.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +"""Argparse surface for the Yao CLI.""" + +import argparse +from collections.abc import Callable + +from yao_cli_parser_evidence import add_evidence_commands +from yao_cli_parser_operator import add_operator_commands +from yao_cli_parser_operations import add_operating_loop_commands + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py to keep CLI parser declarations separate from command orchestration." + + +def _handler(command_handlers: dict[str, Callable[[argparse.Namespace], int]], name: str) -> Callable[[argparse.Namespace], int]: + if name not in command_handlers: + raise KeyError(f"Missing CLI command handler: {name}") + return command_handlers[name] + + +def build_parser(command_handlers: dict[str, Callable[[argparse.Namespace], int]]) -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Unified authoring CLI for yao-meta-skill.") + subparsers = parser.add_subparsers(dest="command", required=True) + + init_cmd = subparsers.add_parser("init", help="Initialize a minimal skill package.") + init_cmd.add_argument("name") + init_cmd.add_argument("--description", default="Describe what the skill does and when to use it.") + init_cmd.add_argument("--title") + init_cmd.add_argument("--output-dir", default=".") + init_cmd.add_argument("--mode", choices=["scaffold", "production", "library", "governed"], default="scaffold") + init_cmd.add_argument("--archetype", choices=["scaffold", "production", "library", "governed"], default="scaffold") + init_cmd.add_argument("--external-reference", action="append", default=[]) + init_cmd.add_argument("--user-reference", action="append", default=[]) + init_cmd.add_argument("--local-constraint", action="append", default=[]) + init_cmd.add_argument("--github-query") + init_cmd.add_argument("--github-top-n", type=int, default=3) + init_cmd.add_argument("--github-fixture-dir") + init_cmd.add_argument("--intent-job") + init_cmd.add_argument("--intent-real-input", action="append", default=[]) + init_cmd.add_argument("--intent-primary-output") + init_cmd.add_argument("--intent-exclusion", action="append", default=[]) + init_cmd.add_argument("--intent-constraint", action="append", default=[]) + init_cmd.add_argument("--intent-standard", action="append", default=[]) + init_cmd.add_argument("--intent-correction") + init_cmd.set_defaults(func=_handler(command_handlers, "command_init")) + + quickstart_cmd = subparsers.add_parser( + "quickstart", + help="Interactive fast path for creating a scaffold-first skill package.", + ) + quickstart_cmd.add_argument("--name") + quickstart_cmd.add_argument("--job") + quickstart_cmd.add_argument("--real-input", action="append", default=[]) + quickstart_cmd.add_argument("--primary-output") + quickstart_cmd.add_argument("--description") + quickstart_cmd.add_argument("--title") + quickstart_cmd.add_argument("--output-dir", default=".") + quickstart_cmd.add_argument("--mode", choices=["scaffold", "production", "library", "governed"]) + quickstart_cmd.add_argument("--archetype", choices=["scaffold", "production", "library", "governed"]) + quickstart_cmd.add_argument("--external-reference", action="append", default=[]) + quickstart_cmd.add_argument("--user-reference", action="append", default=[]) + quickstart_cmd.add_argument("--local-constraint", action="append", default=[]) + quickstart_cmd.add_argument("--constraint", action="append", default=[]) + quickstart_cmd.add_argument("--github-query") + quickstart_cmd.add_argument("--github-top-n", type=int, default=3) + quickstart_cmd.add_argument("--github-fixture-dir") + quickstart_cmd.add_argument("--no-update-check", action="store_true") + quickstart_cmd.set_defaults(func=_handler(command_handlers, "command_quickstart")) + + validate_cmd = subparsers.add_parser("validate", help="Run validate, lint, governance, and resource checks.") + validate_cmd.add_argument("skill_dir", nargs="?", default=".") + validate_cmd.add_argument("--require-manifest", action="store_true") + validate_cmd.set_defaults(func=_handler(command_handlers, "command_validate")) + + optimize_cmd = subparsers.add_parser("optimize-description", help="Optimize description candidates for a target.") + optimize_cmd.add_argument( + "--target", + choices=["root", "team-frontend-review", "governed-incident-command", "all"], + default="root", + ) + optimize_cmd.add_argument("--write", action="store_true", help="Write default report artifacts for the target.") + optimize_cmd.set_defaults(func=_handler(command_handlers, "command_optimize_description")) + + promote_cmd = subparsers.add_parser("promote-check", help="Apply promotion policy and build iteration bundles.") + promote_cmd.set_defaults(func=_handler(command_handlers, "command_promote_check")) + + python_compat_cmd = subparsers.add_parser( + "python-compat", + help="Check Python source compatibility for the supported CI/runtime interpreter.", + ) + python_compat_cmd.add_argument("skill_dir", nargs="?", default=".") + python_compat_cmd.add_argument("--path", action="append", default=[]) + python_compat_cmd.add_argument("--target-python", default="3.11") + python_compat_cmd.add_argument("--output-json") + python_compat_cmd.add_argument("--output-md") + python_compat_cmd.add_argument("--generated-at") + python_compat_cmd.set_defaults(func=_handler(command_handlers, "command_python_compat")) + + architecture_audit_cmd = subparsers.add_parser( + "architecture-audit", + help="Render maintainability evidence for large Python files and CLI command surface.", + ) + architecture_audit_cmd.add_argument("skill_dir", nargs="?", default=".") + architecture_audit_cmd.add_argument("--output-json") + architecture_audit_cmd.add_argument("--output-md") + architecture_audit_cmd.add_argument("--warn-lines", type=int, default=900) + architecture_audit_cmd.add_argument("--block-lines", type=int, default=1500) + architecture_audit_cmd.add_argument("--generated-at") + architecture_audit_cmd.set_defaults(func=_handler(command_handlers, "command_architecture_audit")) + + review_cmd = subparsers.add_parser("review", help="Locate the current bundle and human review stub for a target.") + review_cmd.add_argument( + "--target", + choices=["root", "team-frontend-review", "governed-incident-command"], + default="root", + ) + review_cmd.set_defaults(func=_handler(command_handlers, "command_review")) + + snapshot_cmd = subparsers.add_parser("release-snapshot", help="Create a versioned snapshot from current promotion outputs.") + snapshot_cmd.add_argument( + "--target", + choices=["root", "team-frontend-review", "governed-incident-command"], + default="root", + ) + snapshot_cmd.add_argument("--label", default="manual") + snapshot_cmd.set_defaults(func=_handler(command_handlers, "command_release_snapshot")) + + flow_cmd = subparsers.add_parser( + "workspace-flow", + help="Run optimize, promotion, review refresh, and release snapshots as one authoring flow.", + ) + flow_cmd.add_argument( + "--target", + choices=["root", "team-frontend-review", "governed-incident-command", "all"], + default="root", + ) + flow_cmd.add_argument("--label", default="manual") + flow_cmd.set_defaults(func=_handler(command_handlers, "command_workspace_flow")) + + report_cmd = subparsers.add_parser("report", help="Render route, iteration, regression, and context reports.") + report_cmd.add_argument("--refresh-optimization", action="store_true") + report_cmd.set_defaults(func=_handler(command_handlers, "command_report")) + + skill_report_cmd = subparsers.add_parser("skill-report", help="Render the HTML skill report for a skill package.") + skill_report_cmd.add_argument("skill_dir", nargs="?", default=".") + skill_report_cmd.add_argument("--output-html") + skill_report_cmd.add_argument("--output-json") + skill_report_cmd.set_defaults(func=_handler(command_handlers, "command_skill_report")) + + skill_interpretation_cmd = subparsers.add_parser( + "skill-interpretation", + help="Render the first-class skill interpretation report for a skill package.", + ) + skill_interpretation_cmd.add_argument("skill_dir", nargs="?", default=".") + skill_interpretation_cmd.add_argument("--output-html") + skill_interpretation_cmd.add_argument("--output-json") + skill_interpretation_cmd.set_defaults(func=_handler(command_handlers, "command_skill_interpretation")) + + review_viewer_cmd = subparsers.add_parser("review-viewer", help="Render a compact HTML review page for a skill package.") + review_viewer_cmd.add_argument("skill_dir", nargs="?", default=".") + review_viewer_cmd.add_argument("--output-html") + review_viewer_cmd.add_argument("--output-json") + review_viewer_cmd.set_defaults(func=_handler(command_handlers, "command_review_viewer")) + + review_studio_cmd = subparsers.add_parser("review-studio", help="Render Review Studio 2.0 for a skill package.") + review_studio_cmd.add_argument("skill_dir", nargs="?", default=".") + review_studio_cmd.add_argument("--output-html") + review_studio_cmd.add_argument("--output-json") + review_studio_cmd.set_defaults(func=_handler(command_handlers, "command_review_studio")) + + skill_os2_audit_cmd = subparsers.add_parser( + "skill-os2-audit", + help="Render a requirement-by-requirement Skill OS 2.0 completion audit.", + ) + skill_os2_audit_cmd.add_argument("skill_dir", nargs="?", default=".") + skill_os2_audit_cmd.add_argument("--output-json") + skill_os2_audit_cmd.add_argument("--output-md") + skill_os2_audit_cmd.add_argument("--generated-at") + skill_os2_audit_cmd.set_defaults(func=_handler(command_handlers, "command_skill_os2_audit")) + + skill_os2_coverage_cmd = subparsers.add_parser( + "skill-os2-coverage", + help="Render Skill OS 2.0 blueprint-to-evidence coverage.", + ) + skill_os2_coverage_cmd.add_argument("skill_dir", nargs="?", default=".") + skill_os2_coverage_cmd.add_argument("--output-json") + skill_os2_coverage_cmd.add_argument("--output-md") + skill_os2_coverage_cmd.add_argument("--generated-at") + skill_os2_coverage_cmd.set_defaults(func=_handler(command_handlers, "command_skill_os2_coverage")) + + add_evidence_commands(subparsers, command_handlers) + + reference_scan_cmd = subparsers.add_parser( + "reference-scan", + help="Render a controlled benchmark scan report for a skill package.", + ) + reference_scan_cmd.add_argument("skill_dir", nargs="?", default=".") + reference_scan_cmd.add_argument("--reference", action="append", default=[]) + reference_scan_cmd.add_argument("--external-reference", action="append", default=[]) + reference_scan_cmd.add_argument("--user-reference", action="append", default=[]) + reference_scan_cmd.add_argument("--local-constraint", action="append", default=[]) + reference_scan_cmd.add_argument("--output-md") + reference_scan_cmd.add_argument("--output-json") + reference_scan_cmd.set_defaults(func=_handler(command_handlers, "command_reference_scan")) + + github_scan_cmd = subparsers.add_parser( + "github-benchmark-scan", + help="Search top public GitHub repositories and extract borrow or avoid patterns for a skill.", + ) + github_scan_cmd.add_argument("skill_dir", nargs="?", default=".") + github_scan_cmd.add_argument("--query", required=True) + github_scan_cmd.add_argument("--top-n", type=int, default=3) + github_scan_cmd.add_argument("--fixture-dir") + github_scan_cmd.add_argument("--output-md") + github_scan_cmd.add_argument("--output-json") + github_scan_cmd.set_defaults(func=_handler(command_handlers, "command_github_benchmark_scan")) + + intent_confidence_cmd = subparsers.add_parser( + "intent-confidence", + help="Render a confidence report for how well the real job, inputs, outputs, and exclusions are understood.", + ) + intent_confidence_cmd.add_argument("skill_dir", nargs="?", default=".") + intent_confidence_cmd.add_argument("--context-json") + intent_confidence_cmd.add_argument("--output-md") + intent_confidence_cmd.add_argument("--output-json") + intent_confidence_cmd.set_defaults(func=_handler(command_handlers, "command_intent_confidence")) + + intent_dialogue_cmd = subparsers.add_parser( + "intent-dialogue", + help="Render a front-loaded intent dialogue guide for a skill package.", + ) + intent_dialogue_cmd.add_argument("skill_dir", nargs="?", default=".") + intent_dialogue_cmd.add_argument("--output-md") + intent_dialogue_cmd.add_argument("--output-json") + intent_dialogue_cmd.set_defaults(func=_handler(command_handlers, "command_intent_dialogue")) + + reference_synthesis_cmd = subparsers.add_parser( + "reference-synthesis", + help="Render a multi-source reference synthesis report for a skill package.", + ) + reference_synthesis_cmd.add_argument("skill_dir", nargs="?", default=".") + reference_synthesis_cmd.add_argument("--output-md") + reference_synthesis_cmd.add_argument("--output-json") + reference_synthesis_cmd.set_defaults(func=_handler(command_handlers, "command_reference_synthesis")) + + output_risk_cmd = subparsers.add_parser( + "output-risk-profile", + help="Render predicted output failure modes and self-repair checks for a skill package.", + ) + output_risk_cmd.add_argument("skill_dir", nargs="?", default=".") + output_risk_cmd.add_argument("--output-md") + output_risk_cmd.add_argument("--output-json") + output_risk_cmd.set_defaults(func=_handler(command_handlers, "command_output_risk_profile")) + + artifact_design_cmd = subparsers.add_parser( + "artifact-design-profile", + help="Render artifact design direction and visual quality gates for a skill package.", + ) + artifact_design_cmd.add_argument("skill_dir", nargs="?", default=".") + artifact_design_cmd.add_argument("--output-md") + artifact_design_cmd.add_argument("--output-json") + artifact_design_cmd.set_defaults(func=_handler(command_handlers, "command_artifact_design_profile")) + + prompt_quality_cmd = subparsers.add_parser( + "prompt-quality-profile", + help="Render prompt-facing need model, RTF mapping, complexity, and quality checks for a skill package.", + ) + prompt_quality_cmd.add_argument("skill_dir", nargs="?", default=".") + prompt_quality_cmd.add_argument("--output-md") + prompt_quality_cmd.add_argument("--output-json") + prompt_quality_cmd.set_defaults(func=_handler(command_handlers, "command_prompt_quality_profile")) + + system_model_cmd = subparsers.add_parser( + "system-model", + help="Render a systems-thinking model for boundaries, feedback loops, drift, and leverage points.", + ) + system_model_cmd.add_argument("skill_dir", nargs="?", default=".") + system_model_cmd.add_argument("--output-md") + system_model_cmd.add_argument("--output-json") + system_model_cmd.set_defaults(func=_handler(command_handlers, "command_system_model")) + + iteration_directions_cmd = subparsers.add_parser( + "iteration-directions", + help="Render the top three next iteration directions for a skill package.", + ) + iteration_directions_cmd.add_argument("skill_dir", nargs="?", default=".") + iteration_directions_cmd.add_argument("--output-md") + iteration_directions_cmd.add_argument("--output-json") + iteration_directions_cmd.set_defaults(func=_handler(command_handlers, "command_iteration_directions")) + + feedback_cmd = subparsers.add_parser( + "feedback", + help="Capture lightweight reviewer feedback without running the full promotion flow.", + ) + feedback_cmd.add_argument("skill_dir", nargs="?", default=".") + feedback_cmd.add_argument("--note") + feedback_cmd.add_argument("--rating", type=int, default=3) + feedback_cmd.add_argument("--category", default="general") + feedback_cmd.add_argument("--recommended-action", default="review") + feedback_cmd.set_defaults(func=_handler(command_handlers, "command_feedback")) + + add_operator_commands(subparsers, command_handlers) + add_operating_loop_commands(subparsers, command_handlers) + + baseline_compare_cmd = subparsers.add_parser( + "baseline-compare", + help="Render a lightweight with-skill vs baseline comparison across tracked targets.", + ) + baseline_compare_cmd.set_defaults(func=_handler(command_handlers, "command_baseline_compare")) + + skill_ir_cmd = subparsers.add_parser("skill-ir", help="Export platform-neutral Skill IR for a skill package.") + skill_ir_cmd.add_argument("skill_dir", nargs="?", default=".") + skill_ir_cmd.add_argument("--output-json") + skill_ir_cmd.add_argument("--validate-only", action="store_true") + skill_ir_cmd.set_defaults(func=_handler(command_handlers, "command_skill_ir")) + + compile_skill_cmd = subparsers.add_parser( + "compile-skill", + help="Compile Skill IR into target-specific semantic contracts.", + ) + compile_skill_cmd.add_argument("skill_dir", nargs="?", default=".") + compile_skill_cmd.add_argument("--target", action="append") + compile_skill_cmd.add_argument("--output-json") + compile_skill_cmd.add_argument("--output-md") + compile_skill_cmd.add_argument("--generated-at") + compile_skill_cmd.set_defaults(func=_handler(command_handlers, "command_compile_skill")) + + output_eval_cmd = subparsers.add_parser("output-eval", help="Run Output Eval Lab assertion grading.") + output_eval_cmd.add_argument("--cases") + output_eval_cmd.add_argument("--output-json") + output_eval_cmd.add_argument("--output-md") + output_eval_cmd.add_argument("--blind-pack-json") + output_eval_cmd.add_argument("--blind-pack-md") + output_eval_cmd.add_argument("--blind-answer-key-json") + output_eval_cmd.set_defaults(func=_handler(command_handlers, "command_output_eval")) + + output_review_kit_cmd = subparsers.add_parser( + "output-review-kit", + help="Prepare a reviewer-facing blind A/B output review kit without exposing the answer key.", + ) + output_review_kit_cmd.add_argument("--blind-pack-json") + output_review_kit_cmd.add_argument("--blind-pack-md") + output_review_kit_cmd.add_argument("--decisions") + output_review_kit_cmd.add_argument("--output-json") + output_review_kit_cmd.add_argument("--output-md") + output_review_kit_cmd.add_argument("--output-html") + output_review_kit_cmd.add_argument("--write-template", action="store_true") + output_review_kit_cmd.set_defaults(func=_handler(command_handlers, "command_output_review_kit")) + + output_execution_cmd = subparsers.add_parser( + "output-exec", + help="Record output-eval execution evidence, timing, and token usage.", + ) + output_execution_cmd.add_argument("--cases") + output_execution_cmd.add_argument("--output-json") + output_execution_cmd.add_argument("--output-md") + output_execution_cmd.add_argument("--runner-command") + output_execution_cmd.add_argument( + "--provider-runner", + choices=["openai", "deepseek"], + help="Use the bundled provider-backed runner instead of a custom runner command.", + ) + output_execution_cmd.add_argument("--provider-model", help="Model for --provider-runner; otherwise use YAO_OUTPUT_EVAL_MODEL.") + output_execution_cmd.add_argument("--provider-base-url", help="Override provider endpoint for compatible APIs.") + output_execution_cmd.add_argument( + "--provider-api-format", + choices=["responses", "chat-completions"], + help="Provider API shape used by the bundled provider runner. Defaults are selected from --provider-runner.", + ) + output_execution_cmd.add_argument( + "--provider-thinking", + choices=["enabled", "disabled"], + help="Optional thinking mode override for chat-completions providers that support it.", + ) + output_execution_cmd.add_argument( + "--provider-temperature", + type=float, + help="Sampling temperature for the bundled provider runner. Defaults to 0 for reproducibility.", + ) + output_execution_cmd.add_argument("--api-key-env") + output_execution_cmd.add_argument("--allow-insecure-localhost", action="store_true") + output_execution_cmd.add_argument("--allow-custom-base-url", action="store_true") + output_execution_cmd.add_argument("--timeout-seconds", type=float) + output_execution_cmd.set_defaults(func=_handler(command_handlers, "command_output_execution")) + + output_review_cmd = subparsers.add_parser( + "output-review", + help="Adjudicate blind A/B output review decisions against the answer key.", + ) + output_review_cmd.add_argument("--blind-pack") + output_review_cmd.add_argument("--answer-key") + output_review_cmd.add_argument("--decisions") + output_review_cmd.add_argument("--output-json") + output_review_cmd.add_argument("--output-md") + output_review_cmd.add_argument("--write-template", action="store_true") + output_review_cmd.set_defaults(func=_handler(command_handlers, "command_output_review")) + + output_review_import_cmd = subparsers.add_parser( + "output-review-import", + help="Import human blind A/B reviewer decisions into the canonical decision file.", + ) + output_review_import_cmd.add_argument("--input", required=True) + output_review_import_cmd.add_argument("--format", choices=["auto", "json", "jsonl", "csv"], default="auto") + output_review_import_cmd.add_argument("--blind-pack") + output_review_import_cmd.add_argument("--output-json") + output_review_import_cmd.add_argument("--reviewer") + output_review_import_cmd.add_argument("--reviewed-at") + output_review_import_cmd.add_argument("--blind-review-attested", action="store_true") + output_review_import_cmd.add_argument("--run-adjudication", action="store_true") + output_review_import_cmd.add_argument("--answer-key") + output_review_import_cmd.add_argument("--adjudication-json") + output_review_import_cmd.add_argument("--adjudication-md") + output_review_import_cmd.set_defaults(func=_handler(command_handlers, "command_output_review_import")) + + conformance_cmd = subparsers.add_parser("conformance", help="Run runtime conformance checks for Skill OS targets.") + conformance_cmd.add_argument("skill_dir", nargs="?", default=".") + conformance_cmd.add_argument("--target", action="append", choices=["openai", "claude", "agent-skills", "vscode", "generic"]) + conformance_cmd.add_argument("--output-json") + conformance_cmd.add_argument("--output-md") + conformance_cmd.set_defaults(func=_handler(command_handlers, "command_conformance")) + + runtime_permissions_cmd = subparsers.add_parser( + "runtime-permissions", + help="Probe generated target adapters for runtime permission enforcement metadata.", + ) + runtime_permissions_cmd.add_argument("skill_dir", nargs="?", default=".") + runtime_permissions_cmd.add_argument("--package-dir", default="dist") + runtime_permissions_cmd.add_argument("--target", action="append", choices=["openai", "claude", "generic", "vscode"]) + runtime_permissions_cmd.add_argument("--install-simulation-json") + runtime_permissions_cmd.add_argument("--output-json") + runtime_permissions_cmd.add_argument("--output-md") + runtime_permissions_cmd.set_defaults(func=_handler(command_handlers, "command_runtime_permissions")) + + trust_cmd = subparsers.add_parser("trust", help="Run trust and security checks for a skill package.") + trust_cmd.add_argument("skill_dir", nargs="?", default=".") + trust_cmd.add_argument("--output-json") + trust_cmd.add_argument("--output-md") + trust_cmd.set_defaults(func=_handler(command_handlers, "command_trust")) + + skill_atlas_cmd = subparsers.add_parser("skill-atlas", help="Build a portfolio-level Skill Atlas for a workspace.") + skill_atlas_cmd.add_argument("--workspace-root", default=".") + skill_atlas_cmd.add_argument("--output-dir") + skill_atlas_cmd.add_argument("--report-html") + skill_atlas_cmd.add_argument("--report-json") + skill_atlas_cmd.add_argument("--overlap-threshold", type=float) + skill_atlas_cmd.add_argument("--today") + skill_atlas_cmd.set_defaults(func=_handler(command_handlers, "command_skill_atlas")) + + registry_audit_cmd = subparsers.add_parser("registry-audit", help="Build and audit Skill OS registry package metadata.") + registry_audit_cmd.add_argument("skill_dir", nargs="?", default=".") + registry_audit_cmd.add_argument("--registry-dir") + registry_audit_cmd.add_argument("--output-json") + registry_audit_cmd.add_argument("--output-md") + registry_audit_cmd.add_argument("--generated-at") + registry_audit_cmd.set_defaults(func=_handler(command_handlers, "command_registry_audit")) + + package_cmd = subparsers.add_parser("package", help="Export compatibility artifacts for selected targets.") + package_cmd.add_argument("skill_dir", nargs="?", default=".") + package_cmd.add_argument("--platform", action="append") + package_cmd.add_argument("--output-dir", default="dist") + package_cmd.add_argument("--expectations") + package_cmd.add_argument("--zip", action="store_true") + package_cmd.set_defaults(func=_handler(command_handlers, "command_package")) + + package_verify_cmd = subparsers.add_parser("package-verify", help="Verify generated package artifacts, archive safety, and registry parity.") + package_verify_cmd.add_argument("skill_dir", nargs="?", default=".") + package_verify_cmd.add_argument("--package-dir", default="dist") + package_verify_cmd.add_argument("--expectations", default="evals/packaging_expectations.json") + package_verify_cmd.add_argument("--registry-json", default="reports/registry_audit.json") + package_verify_cmd.add_argument("--output-json") + package_verify_cmd.add_argument("--output-md") + package_verify_cmd.add_argument("--require-zip", action="store_true") + package_verify_cmd.add_argument("--generated-at") + package_verify_cmd.set_defaults(func=_handler(command_handlers, "command_package_verify")) + + install_simulate_cmd = subparsers.add_parser("install-simulate", help="Simulate installing a generated package into a temporary local skill root.") + install_simulate_cmd.add_argument("skill_dir", nargs="?", default=".") + install_simulate_cmd.add_argument("--package-dir", default="dist") + install_simulate_cmd.add_argument("--install-root") + install_simulate_cmd.add_argument("--output-json") + install_simulate_cmd.add_argument("--output-md") + install_simulate_cmd.add_argument("--generated-at") + install_simulate_cmd.set_defaults(func=_handler(command_handlers, "command_install_simulate")) + + upgrade_check_cmd = subparsers.add_parser("upgrade-check", help="Compare current and previous registry package metadata for upgrade readiness.") + upgrade_check_cmd.add_argument("skill_dir", nargs="?", default=".") + upgrade_check_cmd.add_argument("--previous-package-json", required=True) + upgrade_check_cmd.add_argument("--current-package-json", default="reports/registry_audit.json") + upgrade_check_cmd.add_argument("--output-json") + upgrade_check_cmd.add_argument("--output-md") + upgrade_check_cmd.add_argument("--generated-at") + upgrade_check_cmd.set_defaults(func=_handler(command_handlers, "command_upgrade_check")) + + test_cmd = subparsers.add_parser("test", help="Run a Makefile test target.") + test_cmd.add_argument("--target", default="test") + test_cmd.set_defaults(func=_handler(command_handlers, "command_test")) + + update_cmd = subparsers.add_parser("check-update", help="Check whether a newer yao-meta-skill version is available.") + update_cmd.add_argument("--force", action="store_true") + update_cmd.add_argument("--no-cache", action="store_true") + update_cmd.add_argument("--version-url") + update_cmd.add_argument("--manifest-url") + update_cmd.add_argument("--timeout", type=float, default=3.0) + update_cmd.add_argument("--allow-custom-update-url", action="store_true") + update_cmd.set_defaults(func=_handler(command_handlers, "command_check_update")) + + return parser diff --git a/scripts/yao_cli_parser_evidence.py b/scripts/yao_cli_parser_evidence.py new file mode 100644 index 0000000..e4d6c12 --- /dev/null +++ b/scripts/yao_cli_parser_evidence.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +"""Evidence command declarations for the Yao CLI parser.""" + +import argparse +from collections.abc import Callable + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao_cli_parser.py to keep evidence command declarations out of the main parser module." + + +def _handler(command_handlers: dict[str, Callable[[argparse.Namespace], int]], name: str) -> Callable[[argparse.Namespace], int]: + if name not in command_handlers: + raise KeyError(f"Missing CLI command handler: {name}") + return command_handlers[name] + + +def add_evidence_commands( + subparsers: argparse._SubParsersAction, + command_handlers: dict[str, Callable[[argparse.Namespace], int]], +) -> None: + world_class_evidence_cmd = subparsers.add_parser( + "world-class-evidence", + help="Render the evidence collection plan for remaining world-class readiness gaps.", + ) + world_class_evidence_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_evidence_cmd.add_argument("--output-json") + world_class_evidence_cmd.add_argument("--output-md") + world_class_evidence_cmd.add_argument("--generated-at") + world_class_evidence_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_evidence")) + + world_class_ledger_cmd = subparsers.add_parser( + "world-class-ledger", + help="Render the machine-checkable ledger for world-class evidence gaps.", + ) + world_class_ledger_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_ledger_cmd.add_argument("--output-json") + world_class_ledger_cmd.add_argument("--output-md") + world_class_ledger_cmd.add_argument("--submissions-dir") + world_class_ledger_cmd.add_argument("--generated-at") + world_class_ledger_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_ledger")) + + world_class_intake_cmd = subparsers.add_parser( + "world-class-intake", + help="Validate world-class human and external evidence intake packets.", + ) + world_class_intake_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_intake_cmd.add_argument("--submissions-dir") + world_class_intake_cmd.add_argument("--output-json") + world_class_intake_cmd.add_argument("--output-md") + world_class_intake_cmd.add_argument("--generated-at") + world_class_intake_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_intake")) + + world_class_preflight_cmd = subparsers.add_parser( + "world-class-preflight", + help="Render operator preflight checks for collecting pending world-class evidence.", + ) + world_class_preflight_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_preflight_cmd.add_argument("--submissions-dir") + world_class_preflight_cmd.add_argument("--output-json") + world_class_preflight_cmd.add_argument("--output-md") + world_class_preflight_cmd.add_argument("--output-html") + world_class_preflight_cmd.add_argument("--generated-at") + world_class_preflight_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_preflight")) + + world_class_submission_kit_cmd = subparsers.add_parser( + "world-class-submission-kit", + help="Prepare editable world-class evidence submission drafts.", + ) + world_class_submission_kit_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_submission_kit_cmd.add_argument("--output-dir") + world_class_submission_kit_cmd.add_argument("--evidence-key", action="append", default=[]) + world_class_submission_kit_cmd.add_argument("--overwrite", action="store_true") + world_class_submission_kit_cmd.add_argument( + "--prefill-artifacts", + action="store_true", + help="Insert SHA-256 digests for currently available aggregate artifacts while keeping drafts template-only.", + ) + world_class_submission_kit_cmd.add_argument("--generated-at") + world_class_submission_kit_cmd.add_argument("--output-html") + world_class_submission_kit_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_submission_kit")) + + world_class_submission_review_cmd = subparsers.add_parser( + "world-class-submission-review", + help="Render a read-only review queue for world-class evidence submissions.", + ) + world_class_submission_review_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_submission_review_cmd.add_argument("--submissions-dir") + world_class_submission_review_cmd.add_argument("--output-json") + world_class_submission_review_cmd.add_argument("--output-md") + world_class_submission_review_cmd.add_argument("--generated-at") + world_class_submission_review_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_submission_review")) + + world_class_runbook_cmd = subparsers.add_parser( + "world-class-runbook", + help="Render an operator runbook for pending world-class evidence.", + ) + world_class_runbook_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_runbook_cmd.add_argument("--submissions-dir") + world_class_runbook_cmd.add_argument("--output-json") + world_class_runbook_cmd.add_argument("--output-md") + world_class_runbook_cmd.add_argument("--output-html") + world_class_runbook_cmd.add_argument("--generated-at") + world_class_runbook_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_runbook")) + + world_class_claim_guard_cmd = subparsers.add_parser( + "world-class-claim-guard", + help="Scan public claim surfaces for premature world-class completion claims.", + ) + world_class_claim_guard_cmd.add_argument("skill_dir", nargs="?", default=".") + world_class_claim_guard_cmd.add_argument("--claim-surface", action="append", default=[]) + world_class_claim_guard_cmd.add_argument("--output-json") + world_class_claim_guard_cmd.add_argument("--output-md") + world_class_claim_guard_cmd.add_argument("--generated-at") + world_class_claim_guard_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_claim_guard")) + + benchmark_reproducibility_cmd = subparsers.add_parser( + "benchmark-reproducibility", + help="Render benchmark methodology, artifact, failure-disclosure, and reproduction-command evidence.", + ) + benchmark_reproducibility_cmd.add_argument("skill_dir", nargs="?", default=".") + benchmark_reproducibility_cmd.add_argument("--output-json") + benchmark_reproducibility_cmd.add_argument("--output-md") + benchmark_reproducibility_cmd.add_argument("--generated-at") + benchmark_reproducibility_cmd.set_defaults(func=_handler(command_handlers, "command_benchmark_reproducibility")) + + evidence_consistency_cmd = subparsers.add_parser( + "evidence-consistency", + help="Render cross-report evidence consistency checks.", + ) + evidence_consistency_cmd.add_argument("skill_dir", nargs="?", default=".") + evidence_consistency_cmd.add_argument("--output-json") + evidence_consistency_cmd.add_argument("--output-md") + evidence_consistency_cmd.add_argument("--generated-at") + evidence_consistency_cmd.set_defaults(func=_handler(command_handlers, "command_evidence_consistency")) diff --git a/scripts/yao_cli_parser_operations.py b/scripts/yao_cli_parser_operations.py new file mode 100644 index 0000000..263e253 --- /dev/null +++ b/scripts/yao_cli_parser_operations.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +"""Operating-loop command declarations for the Yao CLI parser.""" + +import argparse +from collections.abc import Callable + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao_cli_parser.py to keep SkillOps, telemetry, and review-loop command declarations out of the main parser module." + + +TELEMETRY_EVENT_CHOICES = ["review_event", "script_run", "skill_activation", "skill_output"] +TELEMETRY_ACTIVATION_CHOICES = ["explicit", "implicit", "manual", "unknown"] +TELEMETRY_OUTCOME_CHOICES = ["accepted", "edited", "failed", "missed", "rejected", "reviewed", "unknown"] +TELEMETRY_FAILURE_CHOICES = [ + "bad_output", + "missing_resource", + "none", + "review_overdue", + "script_error", + "under_trigger", + "wrong_trigger", +] +TELEMETRY_SOURCE_CHOICES = ["external", "manual", "unknown", "yao_cli"] + +REVIEW_WAIVER_GATE_CHOICES = [ + "architecture-maintainability", + "context-budget", + "intent-canvas", + "operations-loop", + "output-lab", + "python-compat", + "registry-audit", + "release-notes", + "runtime-matrix", + "skill-atlas", + "trigger-lab", + "trust-report", + "permission-gates", + "permission-runtime", +] + +REVIEW_ANNOTATION_GATE_CHOICES = [ + *REVIEW_WAIVER_GATE_CHOICES[:8], + "review-waivers", + *REVIEW_WAIVER_GATE_CHOICES[8:12], + "world-class-evidence", + *REVIEW_WAIVER_GATE_CHOICES[12:], +] + + +def _handler(command_handlers: dict[str, Callable[[argparse.Namespace], int]], name: str) -> Callable[[argparse.Namespace], int]: + if name not in command_handlers: + raise KeyError(f"Missing CLI command handler: {name}") + return command_handlers[name] + + +def _add_telemetry_metadata_arguments( + command: argparse.ArgumentParser, + *, + source_default: str, + command_default: str, + activation_default: str, + event_flag: str | None = None, + event_default: str | None = None, +) -> None: + if event_flag is not None: + kwargs = {"choices": TELEMETRY_EVENT_CHOICES} + if event_default is not None: + kwargs["default"] = event_default + command.add_argument(event_flag, **kwargs) + command.add_argument("--activation-type", choices=TELEMETRY_ACTIVATION_CHOICES, default=activation_default) + command.add_argument("--outcome", choices=TELEMETRY_OUTCOME_CHOICES, default="unknown") + command.add_argument("--failure-type", choices=TELEMETRY_FAILURE_CHOICES, default="none") + command.add_argument("--source", choices=TELEMETRY_SOURCE_CHOICES, default=source_default) + command.add_argument("--command", dest="telemetry_command", default=command_default) + command.add_argument("--timestamp") + command.add_argument("--skill-name") + command.add_argument("--version") + + +def add_operating_loop_commands( + subparsers: argparse._SubParsersAction, + command_handlers: dict[str, Callable[[argparse.Namespace], int]], +) -> None: + adapt_scan_cmd = subparsers.add_parser( + "adapt-scan", + help="Scan one explicit local source file for redacted repeated user preference signals.", + ) + adapt_scan_cmd.add_argument("skill_dir", nargs="?", default=".") + adapt_scan_cmd.add_argument("--source", required=True) + adapt_scan_cmd.add_argument("--output-json") + adapt_scan_cmd.add_argument("--output-md") + adapt_scan_cmd.add_argument("--min-support", type=int, default=2) + adapt_scan_cmd.add_argument("--generated-at") + adapt_scan_cmd.add_argument("--allow-history-source", action="store_true") + adapt_scan_cmd.set_defaults(func=_handler(command_handlers, "command_adapt_scan")) + + adapt_propose_cmd = subparsers.add_parser( + "adapt-propose", + help="Create proposal-only adaptation plans from redacted repeated preference patterns.", + ) + adapt_propose_cmd.add_argument("skill_dir", nargs="?", default=".") + adapt_propose_cmd.add_argument("--patterns-json") + adapt_propose_cmd.add_argument("--output-json") + adapt_propose_cmd.add_argument("--output-md") + adapt_propose_cmd.add_argument("--generated-at") + adapt_propose_cmd.set_defaults(func=_handler(command_handlers, "command_adapt_propose")) + + adapt_apply_cmd = subparsers.add_parser( + "adapt-apply", + help="Dry-run or apply an approved adaptation patch with allowlist, regression, and rollback evidence.", + ) + adapt_apply_cmd.add_argument("skill_dir", nargs="?", default=".") + adapt_apply_cmd.add_argument("--proposal-id") + adapt_apply_cmd.add_argument("--patch-file") + adapt_apply_cmd.add_argument("--proposals-json") + adapt_apply_cmd.add_argument("--approval-ledger") + adapt_apply_cmd.add_argument("--output-json") + adapt_apply_cmd.add_argument("--output-md") + adapt_apply_cmd.add_argument("--generated-at") + adapt_apply_cmd.add_argument("--today") + adapt_apply_cmd.add_argument("--write-template", action="store_true") + adapt_apply_cmd.add_argument("--prepare-approval", action="store_true") + adapt_apply_cmd.add_argument("--apply", action="store_true") + adapt_apply_cmd.add_argument("--run-verification", action="store_true") + adapt_apply_cmd.add_argument( + "--no-rollback-on-failure", + dest="rollback_on_failure", + action="store_false", + help="Leave an applied patch in place if verification fails. Default is to reverse the patch.", + ) + adapt_apply_cmd.set_defaults(rollback_on_failure=True) + adapt_apply_cmd.set_defaults(func=_handler(command_handlers, "command_adapt_apply")) + + daily_skillops_cmd = subparsers.add_parser( + "daily-skillops", + help="Render a Daily SkillOps report from explicit-source adaptive evidence without scanning private logs or applying patches.", + ) + daily_skillops_cmd.add_argument("skill_dir", nargs="?", default=".") + daily_skillops_cmd.add_argument("--source") + daily_skillops_cmd.add_argument("--patterns-json", default="reports/user_patterns.json") + daily_skillops_cmd.add_argument("--proposals-json", default="reports/adaptation_proposals.json") + daily_skillops_cmd.add_argument("--output-json") + daily_skillops_cmd.add_argument("--output-md") + daily_skillops_cmd.add_argument("--min-support", type=int, default=2) + daily_skillops_cmd.add_argument("--generated-at") + daily_skillops_cmd.add_argument("--allow-history-source", action="store_true") + daily_skillops_cmd.add_argument("--no-refresh-source-reports", action="store_true") + daily_skillops_cmd.set_defaults(func=_handler(command_handlers, "command_daily_skillops")) + + weekly_curator_cmd = subparsers.add_parser( + "weekly-curator", + help="Render a weekly SkillOps curator report from generated daily reports and portfolio evidence.", + ) + weekly_curator_cmd.add_argument("skill_dir", nargs="?", default=".") + weekly_curator_cmd.add_argument("--daily-json", action="append", default=[]) + weekly_curator_cmd.add_argument("--output-json") + weekly_curator_cmd.add_argument("--output-md") + weekly_curator_cmd.add_argument("--generated-at") + weekly_curator_cmd.set_defaults(func=_handler(command_handlers, "command_weekly_curator")) + + adoption_drift_cmd = subparsers.add_parser( + "adoption-drift", + help="Render local-first metadata-only adoption and drift telemetry for a skill package.", + ) + adoption_drift_cmd.add_argument("skill_dir", nargs="?", default=".") + adoption_drift_cmd.add_argument("--events-jsonl") + adoption_drift_cmd.add_argument("--output-json") + adoption_drift_cmd.add_argument("--output-md") + adoption_drift_cmd.add_argument("--generated-at") + _add_telemetry_metadata_arguments( + adoption_drift_cmd, + event_flag="--record-event", + source_default="manual", + command_default="unknown", + activation_default="unknown", + ) + adoption_drift_cmd.set_defaults(func=_handler(command_handlers, "command_adoption_drift")) + + telemetry_import_cmd = subparsers.add_parser( + "telemetry-import", + help="Import external metadata-only telemetry JSONL and refresh the adoption drift report.", + ) + telemetry_import_cmd.add_argument("skill_dir", nargs="?", default=".") + telemetry_import_cmd.add_argument("--input-jsonl", required=True) + telemetry_import_cmd.add_argument("--events-jsonl") + telemetry_import_cmd.add_argument("--output-json") + telemetry_import_cmd.add_argument("--output-md") + telemetry_import_cmd.add_argument("--generated-at") + telemetry_import_cmd.add_argument("--source", choices=TELEMETRY_SOURCE_CHOICES, default="external") + telemetry_import_cmd.add_argument("--command", dest="telemetry_command", default="external-client") + telemetry_import_cmd.add_argument("--dry-run", action="store_true") + telemetry_import_cmd.set_defaults(func=_handler(command_handlers, "command_telemetry_import")) + + telemetry_emit_cmd = subparsers.add_parser( + "telemetry-emit", + help="Emit one metadata-only telemetry event for later import.", + ) + telemetry_emit_cmd.add_argument("skill_dir", nargs="?", default=".") + telemetry_emit_cmd.add_argument("--output-jsonl") + _add_telemetry_metadata_arguments( + telemetry_emit_cmd, + event_flag="--event", + event_default="script_run", + source_default="external", + command_default="external-client", + activation_default="manual", + ) + telemetry_emit_cmd.add_argument("--dry-run", action="store_true") + telemetry_emit_cmd.set_defaults(func=_handler(command_handlers, "command_telemetry_emit")) + + telemetry_hooks_cmd = subparsers.add_parser( + "telemetry-hooks", + help="Render metadata-only telemetry client hook recipes.", + ) + telemetry_hooks_cmd.add_argument("skill_dir", nargs="?", default=".") + telemetry_hooks_cmd.add_argument("--output-json") + telemetry_hooks_cmd.add_argument("--output-md") + telemetry_hooks_cmd.add_argument("--output-jsonl") + telemetry_hooks_cmd.set_defaults(func=_handler(command_handlers, "command_telemetry_hooks")) + + review_waivers_cmd = subparsers.add_parser( + "review-waivers", + help="Render or update human reviewer waiver evidence for Review Studio.", + ) + review_waivers_cmd.add_argument("skill_dir", nargs="?", default=".") + review_waivers_cmd.add_argument("--waivers-json") + review_waivers_cmd.add_argument("--output-json") + review_waivers_cmd.add_argument("--output-md") + review_waivers_cmd.add_argument("--generated-at") + review_waivers_cmd.add_argument("--add-waiver", action="store_true") + review_waivers_cmd.add_argument("--gate-key", choices=REVIEW_WAIVER_GATE_CHOICES) + review_waivers_cmd.add_argument( + "--decision", + choices=["accepted-risk", "false-positive", "temporary-exception"], + default="accepted-risk", + ) + review_waivers_cmd.add_argument("--reviewer") + review_waivers_cmd.add_argument("--reason") + review_waivers_cmd.add_argument("--expires-at") + review_waivers_cmd.add_argument("--created-at") + review_waivers_cmd.add_argument("--evidence") + review_waivers_cmd.add_argument("--scope", default="current-release") + review_waivers_cmd.set_defaults(func=_handler(command_handlers, "command_review_waivers")) + + review_annotations_cmd = subparsers.add_parser( + "review-annotations", + help="Render or update inline reviewer annotations for Review Studio gates and source paths.", + ) + review_annotations_cmd.add_argument("skill_dir", nargs="?", default=".") + review_annotations_cmd.add_argument("--annotations-json") + review_annotations_cmd.add_argument("--output-json") + review_annotations_cmd.add_argument("--output-md") + review_annotations_cmd.add_argument("--write-template", action="store_true") + review_annotations_cmd.add_argument("--add-annotation", action="store_true") + review_annotations_cmd.add_argument("--annotation-id") + review_annotations_cmd.add_argument("--gate-key", choices=REVIEW_ANNOTATION_GATE_CHOICES) + review_annotations_cmd.add_argument("--target-path") + review_annotations_cmd.add_argument("--line", type=int) + review_annotations_cmd.add_argument("--severity", choices=["blocker", "info", "note", "warning"], default="note") + review_annotations_cmd.add_argument("--status", choices=["deferred", "open", "resolved"], default="open") + review_annotations_cmd.add_argument("--reviewer") + review_annotations_cmd.add_argument("--created-at") + review_annotations_cmd.add_argument("--body") + review_annotations_cmd.add_argument("--suggested-action") + review_annotations_cmd.add_argument("--evidence") + review_annotations_cmd.set_defaults(func=_handler(command_handlers, "command_review_annotations")) diff --git a/scripts/yao_cli_parser_operator.py b/scripts/yao_cli_parser_operator.py new file mode 100644 index 0000000..795d927 --- /dev/null +++ b/scripts/yao_cli_parser_operator.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Operator UX command declarations for the Yao CLI parser.""" + +import argparse +from collections.abc import Callable + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao_cli_parser.py to keep operator UX command declarations out of the main parser module." + + +def _handler(command_handlers: dict[str, Callable[[argparse.Namespace], int]], name: str) -> Callable[[argparse.Namespace], int]: + if name not in command_handlers: + raise KeyError(f"Missing CLI command handler: {name}") + return command_handlers[name] + + +def add_operator_commands( + subparsers: argparse._SubParsersAction, + command_handlers: dict[str, Callable[[argparse.Namespace], int]], +) -> None: + install_status_cmd = subparsers.add_parser( + "install-status", + help="Diagnose whether yao-meta-skill is active in Codex/Cortex, .agents, or a disabled mirror.", + ) + install_status_cmd.add_argument("--skill-name", default="yao-meta-skill") + install_status_cmd.add_argument("--expected-source", default=".") + install_status_cmd.add_argument("--codex-root", default="~/.codex/skills") + install_status_cmd.add_argument("--agents-root", default="~/.agents/skills") + install_status_cmd.add_argument("--disabled-root", default="~/.agents/skills.disabled") + install_status_cmd.set_defaults(func=_handler(command_handlers, "command_install_status")) + + localized_docs_cmd = subparsers.add_parser( + "localized-doc-sync-check", + help="Check that localized README docs contain the public homepage sections introduced in README.md.", + ) + localized_docs_cmd.add_argument("--source", default="README.md") + localized_docs_cmd.add_argument("--localized", default="docs/README.zh-CN.md") + localized_docs_cmd.add_argument( + "--pair", + action="append", + default=[], + help="Custom sync marker in key::source-marker::localized-marker form. Replaces defaults when present.", + ) + localized_docs_cmd.add_argument("--output-json") + localized_docs_cmd.add_argument("--output-md") + localized_docs_cmd.set_defaults(func=_handler(command_handlers, "command_localized_doc_sync_check")) + + pr_review_cmd = subparsers.add_parser( + "pr-review-report", + help="Build a read-only GitHub PR review report with mergeability, checks, files, and suggested commands.", + ) + pr_review_cmd.add_argument("pr") + pr_review_cmd.add_argument("--repo") + pr_review_cmd.add_argument("--require-checks", action="store_true") + pr_review_cmd.add_argument("--output-json") + pr_review_cmd.add_argument("--output-md") + pr_review_cmd.set_defaults(func=_handler(command_handlers, "command_pr_review_report")) diff --git a/scripts/yao_cli_report_commands.py b/scripts/yao_cli_report_commands.py new file mode 100644 index 0000000..8920a39 --- /dev/null +++ b/scripts/yao_cli_report_commands.py @@ -0,0 +1,339 @@ +"""Report and evidence command handlers for the Yao CLI.""" + +import argparse +import json +from pathlib import Path + +from yao_cli_config import baseline_compare_args, local_output_runner_command +from yao_cli_runtime import ROOT, allow_report_status, run_adoption_drift_if_source_exists, run_script + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py to keep report and evidence command handlers out of the CLI orchestrator." + + +def emit_result(result: dict) -> int: + print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2)) + return 0 if result["ok"] else 2 + + +def load_json_file(path: Path) -> dict: + if not path.exists(): + return {} + try: + return json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return {} + + +def output_execution_has_provider_evidence() -> bool: + summary = load_json_file(ROOT / "reports" / "output_execution_runs.json").get("summary", {}) + return ( + int(summary.get("model_executed_count", 0) or 0) > 0 + and int(summary.get("timing_observed_count", 0) or 0) > 0 + and int(summary.get("token_observed_count", 0) or 0) > 0 + ) + + +def run_output_execution_refresh() -> dict: + command = f"run_output_execution.py --runner-command {local_output_runner_command()}" + if output_execution_has_provider_evidence(): + return { + "command": f"{command} skipped: provider-backed evidence already present", + "returncode": 0, + "ok": True, + "stdout": "", + "stderr": "", + "payload": { + "ok": True, + "skipped": True, + "reason": "provider-backed output execution evidence already has observed model, timing, and token metadata", + }, + } + return run_script("run_output_execution.py", ["--runner-command", local_output_runner_command()]) + + +def resolved_skill_dir(args: argparse.Namespace) -> str: + return str(Path(args.skill_dir).resolve()) + + +def append_outputs(cmd: list[str], args: argparse.Namespace, *, markdown: bool = True, generated_at: bool = False) -> None: + if getattr(args, "output_html", None): + cmd.extend(["--output-html", args.output_html]) + if getattr(args, "output_json", None): + cmd.extend(["--output-json", args.output_json]) + if markdown and getattr(args, "output_md", None): + cmd.extend(["--output-md", args.output_md]) + if generated_at and getattr(args, "generated_at", None): + cmd.extend(["--generated-at", args.generated_at]) + + +def render_skill_report_command(args: argparse.Namespace, script_name: str, *, markdown: bool = True, generated_at: bool = False) -> int: + cmd = [resolved_skill_dir(args)] + append_outputs(cmd, args, markdown=markdown, generated_at=generated_at) + return emit_result(run_script(script_name, cmd)) + + +def command_report(args: argparse.Namespace) -> int: + steps = [] + if args.refresh_optimization: + steps.append(run_script("run_description_optimization_suite.py", [])) + steps.extend( + [ + run_script("build_confusion_matrix.py", []), + run_script("promotion_checker.py", []), + run_script("render_eval_dashboard.py", []), + run_script("render_intent_confidence.py", [str(ROOT)]), + run_script("render_description_drift_history.py", []), + run_script("render_iteration_ledger.py", []), + run_script("render_baseline_compare.py", baseline_compare_args()), + run_script("render_regression_history.py", []), + run_script("trust_check.py", [str(ROOT)]), + run_script("python_compat_check.py", [str(ROOT)]), + run_script("render_architecture_maintainability.py", [str(ROOT)]), + run_script("render_context_reports.py", []), + run_script("render_portability_report.py", []), + run_script("render_reference_synthesis.py", [str(ROOT)]), + run_script("render_artifact_design_profile.py", [str(ROOT)]), + run_script("render_prompt_quality_profile.py", [str(ROOT)]), + run_script("render_system_model.py", [str(ROOT)]), + run_script("compile_skill.py", [str(ROOT)]), + run_script("run_output_eval.py", []), + run_output_execution_refresh(), + run_script("prepare_output_review_kit.py", []), + run_script("adjudicate_output_review.py", []), + run_adoption_drift_if_source_exists(), + run_script("render_telemetry_hook_recipes.py", [str(ROOT)]), + run_script("render_review_waivers.py", [str(ROOT)]), + run_script("render_review_annotations.py", [str(ROOT)]), + run_script("render_world_class_evidence_plan.py", [str(ROOT)]), + run_script("render_world_class_evidence_ledger.py", [str(ROOT)]), + run_script("render_world_class_evidence_intake.py", [str(ROOT)]), + run_script("render_world_class_preflight.py", [str(ROOT)]), + allow_report_status(run_script("render_world_class_submission_review.py", [str(ROOT)])), + run_script("render_world_class_operator_runbook.py", [str(ROOT)]), + run_script("render_world_class_claim_guard.py", [str(ROOT)]), + run_script("render_daily_skillops_report.py", [str(ROOT)]), + run_script("render_weekly_curator_report.py", [str(ROOT)]), + run_script("render_skill_os2_audit.py", [str(ROOT)]), + run_script("render_skill_os2_coverage.py", [str(ROOT)]), + run_script("render_benchmark_reproducibility.py", [str(ROOT)]), + run_script("render_skill_overview.py", [str(ROOT)]), + run_script("render_skill_interpretation.py", [str(ROOT)]), + run_script("render_review_studio.py", [str(ROOT)]), + run_script("render_evidence_consistency.py", [str(ROOT)]), + run_script("render_review_viewer.py", [str(ROOT)]), + ] + ) + report = { + "ok": all(step["ok"] for step in steps), + "steps": [{"command": step["command"], "ok": step["ok"], "returncode": step["returncode"]} for step in steps], + "artifacts": { + "eval_results": "reports/eval_suite.json", + "route_scorecard": "reports/route_scorecard.json", + "promotion_decisions": "reports/promotion_decisions.json", + "intent_confidence": "reports/intent-confidence.json", + "iteration_ledger": "reports/iteration_ledger.md", + "baseline_compare": "reports/baseline-compare.json", + "regression_history": "reports/regression_history.md", + "context_budget": "reports/context_budget.json", + "portability_score": "reports/portability_score.json", + "security_trust": "reports/security_trust_report.json", + "python_compatibility": "reports/python_compatibility.json", + "architecture_maintainability": "reports/architecture_maintainability.json", + "reference_synthesis": "reports/reference-synthesis.json", + "artifact_design_profile": "reports/artifact-design-profile.json", + "prompt_quality_profile": "reports/prompt-quality-profile.json", + "system_model": "reports/system-model.json", + "compiled_targets": "reports/compiled_targets.json", + "output_execution": "reports/output_execution_runs.json", + "output_review_kit": "reports/output_review_kit.json", + "output_review_kit_html": "reports/output_review_kit.html", + "output_review_adjudication": "reports/output_review_adjudication.json", + "adoption_drift": "reports/adoption_drift_report.json", + "telemetry_hooks": "reports/telemetry_hook_recipes.json", + "review_waivers": "reports/review_waivers.json", + "review_annotations": "reports/review_annotations.json", + "world_class_evidence_plan": "reports/world_class_evidence_plan.json", + "world_class_evidence_ledger": "reports/world_class_evidence_ledger.json", + "world_class_evidence_intake": "reports/world_class_evidence_intake.json", + "world_class_evidence_preflight": "reports/world_class_evidence_preflight.json", + "world_class_submission_review": "reports/world_class_submission_review.json", + "world_class_operator_runbook": "reports/world_class_operator_runbook.json", + "world_class_claim_guard": "reports/world_class_claim_guard.json", + "daily_skillops": "reports/skillops/daily", + "weekly_curator": "reports/skillops/weekly", + "skill_os2_audit": "reports/skill_os2_audit.json", + "skill_os2_coverage": "reports/skill_os2_coverage.json", + "benchmark_reproducibility": "reports/benchmark_reproducibility.json", + "skill_overview": "reports/skill-overview.json", + "skill_interpretation": "reports/skill-interpretation.json", + "skill_interpretation_html": "reports/skill-interpretation.html", + "review_studio": "reports/review-studio.json", + "review_studio_html": "reports/review-studio.html", + "evidence_consistency": "reports/evidence_consistency.json", + "review_viewer": "reports/review-viewer.json", + "review_viewer_html": "reports/review-viewer.html", + }, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + return 0 if report["ok"] else 2 + + +def command_skill_report(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_skill_overview.py", markdown=False) + + +def command_skill_interpretation(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_skill_interpretation.py", markdown=False) + + +def command_review_viewer(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_review_viewer.py", markdown=False) + + +def command_review_studio(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_review_studio.py", markdown=False) + + +def command_skill_os2_audit(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_skill_os2_audit.py", generated_at=True) + + +def command_skill_os2_coverage(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_skill_os2_coverage.py", generated_at=True) + + +def command_world_class_evidence(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_world_class_evidence_plan.py", generated_at=True) + + +def command_world_class_ledger(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + if getattr(args, "submissions_dir", None): + cmd.extend(["--submissions-dir", args.submissions_dir]) + append_outputs(cmd, args, generated_at=True) + return emit_result(run_script("render_world_class_evidence_ledger.py", cmd)) + + +def command_world_class_intake(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + if args.submissions_dir: + cmd.extend(["--submissions-dir", args.submissions_dir]) + append_outputs(cmd, args, generated_at=True) + return emit_result(run_script("render_world_class_evidence_intake.py", cmd)) + + +def command_world_class_preflight(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + if args.submissions_dir: + cmd.extend(["--submissions-dir", args.submissions_dir]) + append_outputs(cmd, args, generated_at=True) + return emit_result(run_script("render_world_class_preflight.py", cmd)) + + +def command_world_class_submission_kit(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + if args.output_dir: + cmd.extend(["--output-dir", args.output_dir]) + for key in args.evidence_key: + cmd.extend(["--evidence-key", key]) + if args.overwrite: + cmd.append("--overwrite") + if args.prefill_artifacts: + cmd.append("--prefill-artifacts") + if args.generated_at: + cmd.extend(["--generated-at", args.generated_at]) + if args.output_html: + cmd.extend(["--output-html", args.output_html]) + return emit_result(run_script("prepare_world_class_submission_kit.py", cmd)) + + +def command_world_class_submission_review(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + if args.submissions_dir: + cmd.extend(["--submissions-dir", args.submissions_dir]) + append_outputs(cmd, args, generated_at=True) + return emit_result(run_script("render_world_class_submission_review.py", cmd)) + + +def command_world_class_runbook(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + if args.submissions_dir: + cmd.extend(["--submissions-dir", args.submissions_dir]) + append_outputs(cmd, args, generated_at=True) + return emit_result(run_script("render_world_class_operator_runbook.py", cmd)) + + +def command_world_class_claim_guard(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + for surface in args.claim_surface: + cmd.extend(["--claim-surface", surface]) + append_outputs(cmd, args, generated_at=True) + return emit_result(run_script("render_world_class_claim_guard.py", cmd)) + + +def command_benchmark_reproducibility(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_benchmark_reproducibility.py", generated_at=True) + + +def command_evidence_consistency(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_evidence_consistency.py", generated_at=True) + + +def command_reference_scan(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + for reference in args.external_reference: + cmd.extend(["--external-reference", reference]) + for reference in args.user_reference: + cmd.extend(["--user-reference", reference]) + for constraint in args.local_constraint: + cmd.extend(["--local-constraint", constraint]) + for reference in args.reference: + cmd.extend(["--reference", reference]) + append_outputs(cmd, args) + return emit_result(run_script("render_reference_scan.py", cmd)) + + +def command_github_benchmark_scan(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args), "--query", args.query, "--top-n", str(args.top_n)] + if args.fixture_dir: + cmd.extend(["--fixture-dir", args.fixture_dir]) + append_outputs(cmd, args) + return emit_result(run_script("github_benchmark_scan.py", cmd)) + + +def command_intent_confidence(args: argparse.Namespace) -> int: + cmd = [resolved_skill_dir(args)] + if args.context_json: + cmd.extend(["--context-json", args.context_json]) + append_outputs(cmd, args) + return emit_result(run_script("render_intent_confidence.py", cmd)) + + +def command_intent_dialogue(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_intent_dialogue.py") + + +def command_reference_synthesis(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_reference_synthesis.py") + + +def command_output_risk_profile(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_output_risk_profile.py") + + +def command_artifact_design_profile(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_artifact_design_profile.py") + + +def command_prompt_quality_profile(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_prompt_quality_profile.py") + + +def command_system_model(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_system_model.py") + + +def command_iteration_directions(args: argparse.Namespace) -> int: + return render_skill_report_command(args, "render_iteration_directions.py") diff --git a/scripts/yao_cli_runtime.py b/scripts/yao_cli_runtime.py new file mode 100644 index 0000000..bbed20c --- /dev/null +++ b/scripts/yao_cli_runtime.py @@ -0,0 +1,75 @@ +"""Shared runtime helpers for the Yao CLI.""" + +import json +import subprocess +import sys +from pathlib import Path + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py and command modules for shared subprocess execution and JSON payload parsing." + +ROOT = Path(__file__).resolve().parent.parent +SCRIPTS = ROOT / "scripts" + + +def script_path(name: str) -> str: + return str(SCRIPTS / name) + + +def load_json_maybe(text: str) -> dict | None: + text = text.strip() + if not text: + return None + try: + return json.loads(text) + except json.JSONDecodeError: + return None + + +def run_script(name: str, args: list[str], cwd: Path | None = None) -> dict: + proc = subprocess.run( + [sys.executable, script_path(name), *args], + cwd=cwd or ROOT, + capture_output=True, + text=True, + ) + payload = load_json_maybe(proc.stdout) + return { + "command": f"{name} {' '.join(args)}".strip(), + "returncode": proc.returncode, + "ok": proc.returncode == 0, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def allow_report_status(result: dict, *, allowed_returncodes: set[int] | None = None) -> dict: + allowed = allowed_returncodes or {2} + if result.get("ok") or result.get("returncode") not in allowed or result.get("payload") is None: + return result + normalized = dict(result) + normalized["ok"] = True + normalized["soft_status_returncode"] = result.get("returncode") + normalized["soft_status_reason"] = "report generated with a non-passing evidence decision" + return normalized + + +def run_adoption_drift_if_source_exists(skill_dir: Path | None = None) -> dict: + target = (skill_dir or ROOT).resolve() + events_path = target / "reports" / "telemetry_events.jsonl" + if events_path.exists(): + return run_script("render_adoption_drift_report.py", [str(target)]) + return { + "command": "render_adoption_drift_report.py skipped: missing reports/telemetry_events.jsonl", + "returncode": 0, + "ok": True, + "stdout": "", + "stderr": "", + "payload": { + "ok": True, + "skipped": True, + "reason": "raw telemetry event logs are local-only; keeping committed adoption_drift_report artifacts", + }, + } diff --git a/scripts/yao_cli_telemetry.py b/scripts/yao_cli_telemetry.py new file mode 100644 index 0000000..4bb54b1 --- /dev/null +++ b/scripts/yao_cli_telemetry.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Metadata-only CLI telemetry helpers for yao.py.""" + +import argparse +import os +import sys +from pathlib import Path +from typing import Any + +from render_adoption_drift_report import append_event, normalize_event, skill_defaults, utc_now + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by yao.py to record opt-in metadata-only CLI run telemetry." + +ENABLE_ENV = "YAO_CLI_TELEMETRY" +EVENTS_ENV = "YAO_CLI_TELEMETRY_EVENTS" +TRUTHY = {"1", "true", "yes", "on"} +FALSY = {"0", "false", "no", "off"} + + +def add_telemetry_args(parser: argparse.ArgumentParser) -> None: + parser.add_argument( + "--record-cli-telemetry", + action="store_true", + help="Record metadata-only yao.py command telemetry to reports/telemetry_events.jsonl.", + ) + parser.add_argument( + "--no-cli-telemetry", + action="store_true", + help="Disable yao.py command telemetry even when YAO_CLI_TELEMETRY is enabled.", + ) + parser.add_argument( + "--telemetry-events-jsonl", + help="Override the local metadata-only telemetry JSONL path.", + ) + + +def telemetry_enabled(args: argparse.Namespace, environ: dict[str, str] | None = None) -> bool: + environ = environ or os.environ + if getattr(args, "no_cli_telemetry", False): + return False + if getattr(args, "record_cli_telemetry", False): + return True + raw = environ.get(ENABLE_ENV, "").strip().lower() + if raw in TRUTHY: + return True + if raw in FALSY: + return False + return False + + +def telemetry_path(root: Path, args: argparse.Namespace, environ: dict[str, str] | None = None) -> Path: + environ = environ or os.environ + configured = getattr(args, "telemetry_events_jsonl", None) or environ.get(EVENTS_ENV) + if configured: + return Path(configured).expanduser().resolve() + return root / "reports" / "telemetry_events.jsonl" + + +def normalize_command_name(value: Any) -> str: + raw = str(value or "unknown") + lowered = raw.strip().lower() + safe = "".join(char for char in lowered if char.isalnum() or char in {"-", "_"}) + return safe[:64] or "unknown" + + +def cli_event(root: Path, args: argparse.Namespace, returncode: int) -> dict[str, str]: + defaults = skill_defaults(root) + ok = returncode == 0 + return { + "event": "script_run", + "skill": defaults["skill"], + "version": defaults["version"], + "activation_type": "manual", + "outcome": "accepted" if ok else "failed", + "failure_type": "none" if ok else "script_error", + "timestamp": utc_now(), + "source": "yao_cli", + "command": normalize_command_name(getattr(args, "command", "unknown")), + } + + +def maybe_record_cli_event(root: Path, args: argparse.Namespace, returncode: int) -> None: + if not telemetry_enabled(args): + return + path = telemetry_path(root, args) + event, failures = normalize_event(cli_event(root, args, returncode), skill_defaults(root), "yao-cli") + if failures or event is None: + sys.stderr.write(f"Telemetry skipped: {'; '.join(failures)}\n") + return + try: + append_event(path, event) + except OSError as exc: + sys.stderr.write(f"Telemetry skipped: {exc}\n") diff --git a/security/dependency_policy.md b/security/dependency_policy.md new file mode 100644 index 0000000..819c2db --- /dev/null +++ b/security/dependency_policy.md @@ -0,0 +1,10 @@ +# Dependency Policy + +Runtime dependencies should be pinned for CI and release gates. + +Current v0 policy: + +- Python CI dependencies belong in `requirements-ci.txt`. +- Release-critical dependencies should use exact pins. +- Generated packages should avoid fetching code during normal execution. +- Any future package manager lockfile should be included in the trust report. diff --git a/security/network_policy.json b/security/network_policy.json new file mode 100644 index 0000000..4411176 --- /dev/null +++ b/security/network_policy.json @@ -0,0 +1,53 @@ +{ + "schema_version": "1.0", + "default_policy": { + "require_https": true, + "custom_hosts": "deny-by-default", + "review_rule": "Network-capable scripts must declare allowed_hosts and justify any explicit custom-host override." + }, + "scripts": { + "scripts/check_update.py": { + "purpose": "Check the public yao-meta-skill GitHub source for a newer VERSION or manifest version.", + "allowed_hosts": [ + "raw.githubusercontent.com" + ], + "allowed_path_prefixes": [ + "/yaojingang/yao-meta-skill/" + ], + "requires_https": true, + "custom_url_policy": "explicit-opt-in-only via --allow-custom-update-url or YAO_ALLOW_CUSTOM_UPDATE_URL=1", + "timeout_seconds": 3, + "auth": "none" + }, + "scripts/github_benchmark_scan.py": { + "purpose": "Search public GitHub repositories and fetch README excerpts as benchmark references for skill creation.", + "allowed_hosts": [ + "api.github.com" + ], + "allowed_path_prefixes": [ + "/search/repositories", + "/repos/" + ], + "requires_https": true, + "custom_url_policy": "not supported", + "timeout_seconds": 15, + "auth": "optional GITHUB_TOKEN or GH_TOKEN for GitHub API rate limits" + }, + "scripts/provider_output_eval_runner.py": { + "purpose": "Call reviewed provider API endpoints to collect provider-backed output-eval model evidence.", + "allowed_hosts": [ + "api.deepseek.com", + "api.openai.com" + ], + "allowed_path_prefixes": [ + "/chat/completions", + "/v1/chat/completions", + "/v1/responses" + ], + "requires_https": true, + "custom_url_policy": "only via --provider-base-url plus --allow-custom-base-url; plain HTTP is restricted to --allow-insecure-localhost for local test servers", + "timeout_seconds": 120, + "auth": "OPENAI_API_KEY or DEEPSEEK_API_KEY by provider default, or a reviewed env var through --api-key-env" + } + } +} diff --git a/security/network_policy.md b/security/network_policy.md new file mode 100644 index 0000000..db29578 --- /dev/null +++ b/security/network_policy.md @@ -0,0 +1,12 @@ +# Network Policy + +Network-capable scripts are allowed only when their outbound boundary is explicit and reviewable. + +Current policy: + +- `scripts/check_update.py` may read version metadata from `raw.githubusercontent.com/yaojingang/yao-meta-skill/` over HTTPS. +- `scripts/github_benchmark_scan.py` may call the GitHub REST API at `api.github.com` over HTTPS. +- Custom update URLs are denied by default and require explicit operator opt-in plus reviewer awareness. +- New network-capable scripts must be added to `security/network_policy.json` before a governed or team-distributed release. + +The trust checker compares HTTPS URL literals in scripts with `security/network_policy.json`. Missing or mismatched host declarations are reported as warnings. diff --git a/security/permission_policy.json b/security/permission_policy.json new file mode 100644 index 0000000..633d959 --- /dev/null +++ b/security/permission_policy.json @@ -0,0 +1,58 @@ +{ + "schema_version": "1.0", + "reviewed_at": "2026-06-13", + "capabilities": { + "network": { + "decision": "approved", + "reviewer": "Yao Team", + "scope": "Only scripts declared in security/network_policy.json may perform outbound requests.", + "reason": "Network use is bounded to public GitHub metadata, benchmark discovery, update checks, and reviewed provider output-eval endpoints, with allowed hosts reviewed in security/network_policy.json.", + "expires_at": "2026-09-30", + "evidence": [ + "security/network_policy.json", + "reports/security_trust_report.md" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + } + }, + "file_write": { + "decision": "approved", + "reviewer": "Yao Team", + "scope": "Local generated reports, registry metadata, temporary test outputs, and package artifacts under the skill workspace.", + "reason": "File writes are required to create auditable reports, compiled target contracts, package artifacts, and local test fixtures; package safety checks exclude generated temp outputs from archives.", + "expires_at": "2026-09-30", + "evidence": [ + "reports/package_verification.md", + "reports/install_simulation.md", + "security/permission_policy.md" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + } + }, + "subprocess": { + "decision": "approved", + "reviewer": "Yao Team", + "scope": "Local deterministic validation, packaging, install simulation, and CI orchestration commands.", + "reason": "Subprocess use is limited to local verification and packaging flows that are surfaced through CLI help smoke tests and CI targets.", + "expires_at": "2026-09-30", + "evidence": [ + "reports/security_trust_report.md", + "reports/review-studio.html" + ], + "target_enforcement": { + "openai": "metadata-only permission_contract in openai.yaml plus adapter.json", + "claude": "adapter.json target_permission_contract and README note", + "generic": "adapter.json target_permission_contract", + "vscode": "adapter.json target_permission_contract plus VS Code workspace-trust note" + } + } + } +} diff --git a/security/permission_policy.md b/security/permission_policy.md new file mode 100644 index 0000000..085531b --- /dev/null +++ b/security/permission_policy.md @@ -0,0 +1,33 @@ +# Permission Policy + +Permission metadata is generated from `reports/security_trust_report.json` and embedded into target adapters at package time. Governed releases also require explicit reviewer approvals in `security/permission_policy.json`. + +Current v0 capabilities: + +- `network`: scripts that perform outbound requests, bounded by `security/network_policy.json`. +- `file_write`: scripts that create, modify, delete, copy, move, or archive local files. +- `subprocess`: scripts that spawn local commands. +- `interactive`: scripts that prompt for user input. + +Each packaged target must carry: + +- `permission_contract`: platform-neutral capability summary and evidence path. +- `target_permission_contract`: target-specific representation, reviewer note, and native-enforcement status. + +Each high-permission approval must include: + +- `decision: approved` +- `reviewer` +- `scope` +- `reason` +- `expires_at` +- `evidence` +- `target_enforcement` for OpenAI, Claude, generic, and VS Code / Copilot packages + +Review Studio exposes this as the `permission-gates` gate. Missing, invalid, or expired approvals block governed mode and stay reviewer-visible in lighter modes. + +Install simulation also enforces this policy after archive extraction. `python3 scripts/simulate_install.py . --package-dir dist` reads the installed `security/permission_policy.json`, reads each installed target adapter's `target_permission_contract.declared_capabilities`, and fails when any target/capability pair lacks an active reviewer approval or target-specific enforcement note. This keeps package distribution from relying only on metadata presence in the source tree. + +After packaging, run `python3 scripts/probe_runtime_permissions.py . --package-dir dist` to generate `reports/runtime_permission_probes.md` and `reports/runtime_permission_probes.json`. The probe also reads `reports/install_simulation.json` when it matches the probed package directory, so reviewers can see local installer enforcement next to target-native enforcement. Review Studio exposes that evidence as the `permission-runtime` gate, separate from approval governance. A passing probe means target adapters make permission handling explicit and auditable; local installer enforcement is supporting distribution evidence, and does not claim client-native enforcement when the target only supports metadata fallback. + +Current targets preserve permission semantics as metadata. They do not enforce permissions at runtime. Any future native installer or client integration must map these capabilities into that client's permission model rather than dropping them. diff --git a/security/script_policy.md b/security/script_policy.md new file mode 100644 index 0000000..b687f9c --- /dev/null +++ b/security/script_policy.md @@ -0,0 +1,13 @@ +# Script Policy + +Scripts should be deterministic, non-destructive by default, and reviewable. + +Minimum expectations: + +- expose a command-line entrypoint or clear function boundary +- prefer `argparse` and `--help` for operator-facing scripts +- keep `python3 scripts/name.py --help` safe, non-mutating, and fast enough for trust smoke checks +- avoid hidden interactive prompts unless the command is explicitly interactive +- state network or filesystem side effects in nearby docs or trust metadata +- declare allowed network hosts in `security/network_policy.json` before team distribution +- emit structured JSON when used by `scripts/yao.py` diff --git a/security/secret_policy.md b/security/secret_policy.md new file mode 100644 index 0000000..1ddf634 --- /dev/null +++ b/security/secret_policy.md @@ -0,0 +1,7 @@ +# Secret Policy + +Skills must not commit credentials, tokens, private keys, or customer secrets. + +The v0 scanner blocks obvious high-risk patterns such as private key blocks, GitHub personal access tokens, Slack bot tokens, AWS access key ids, and OpenAI-style API keys. + +Potential private paths or local-only references should be treated as warnings unless they expose credentials. diff --git a/security/trust_policy.md b/security/trust_policy.md new file mode 100644 index 0000000..4b2ed60 --- /dev/null +++ b/security/trust_policy.md @@ -0,0 +1,21 @@ +# Trust Policy + +Yao Meta Skill treats scripts and distributed skill packages as supply-chain surfaces. + +Governed or team-distributed skills should produce a trust report before release. The report must identify: + +- secret exposure risk +- scripts that lack a visible CLI/help surface +- execution-level CLI `--help` smoke failures +- interactive or network-capable scripts +- bounded host policy for each network-capable script +- reviewer approval for each high-permission capability +- dependency pinning status +- runtime trust metadata +- package integrity hash + +High-risk secrets or unrestricted remote inline execution block governed release. + +Network-capable scripts without a matching `security/network_policy.json` entry remain reviewer-visible warnings until the host boundary is declared. + +High-permission capabilities without valid `security/permission_policy.json` approvals block governed mode. Required approvals must name the reviewer, scope, reason, expiry, evidence, and target-enforcement mapping. diff --git a/skill-ir/README.md b/skill-ir/README.md new file mode 100644 index 0000000..ad33bfa --- /dev/null +++ b/skill-ir/README.md @@ -0,0 +1,24 @@ +# Skill IR + +Skill IR is the platform-neutral capability contract for Yao Meta Skill 2.0. + +The IR keeps core skill semantics stable before any platform-specific packaging: + +- job to be done +- trigger surface and exclusions +- workflow steps and failure modes +- references, scripts, assets, and reports +- trigger and output evaluation plan +- risk and governance metadata + +Generate the root example: + +```bash +python3 scripts/export_skill_ir.py . --output-json skill-ir/examples/yao-meta-skill.json +``` + +Validate the v0 implementation: + +```bash +python3 tests/verify_skill_ir.py +``` diff --git a/skill-ir/examples/yao-meta-skill.json b/skill-ir/examples/yao-meta-skill.json new file mode 100644 index 0000000..5329e5a --- /dev/null +++ b/skill-ir/examples/yao-meta-skill.json @@ -0,0 +1,387 @@ +{ + "schema_version": "2.0.0", + "name": "yao-meta-skill", + "title": "Yao Meta Skill", + "job_to_be_done": "Turn repeated workflows, prompts, transcripts, runbooks, documents, or existing skill packages into routeable, evaluable, packageable, and governable agent skills for personal, team, library, or governed reuse.", + "trigger_surface": { + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "should_trigger": [ + "Create a skill from this repeated workflow.", + "Turn this runbook into a reusable agent skill.", + "Convert this process note into a reusable skill package for the team.", + "Improve this skill description and add evals.", + "Refactor this prompt into a proper skill package.", + "Package this skill for team reuse.", + "Create a meta-skill for packaging internal workflows.", + "Standardize this recurring workflow as a shareable skill package with references." + ], + "should_not_trigger": [ + "Explain what a workflow is.", + "Just explain what a skill is.", + "Summarize this random note.", + "Translate this README into Japanese.", + "Write a product headline for this landing page.", + "Draft a blog title for this article.", + "Rewrite this paragraph more clearly, but do not package anything.", + "Explain the difference between a runbook and a workflow.", + "one-off writing, translation, explanation, or brainstorming requests that do not need a reusable skill", + "general code review or debugging unless the user is packaging that workflow as a skill" + ], + "edge_cases": [ + "Create a one-off prompt for this task.", + "Write a custom answer for this request without creating a skill package.", + "Help me brainstorm process ideas without building a skill.", + "Improve this README but do not turn it into a skill.", + "Make a checklist for this task, but not a reusable skill.", + "Create a reusable-looking checklist, but keep it as a plain note instead of a skill.", + "Review this process note and explain it, no packaging needed.", + "Turn this into a checklist and template, but stop short of making a full skill." + ] + }, + "workflow": { + "steps": [ + "One-off/no reusable process: `Do not create a skill`; `near-neighbor`; require `repeated use` + `reusable output contract`.", + "Capture job, output, exclusions, constraints, standards, lightest fit.", + "Scan up to `3-5` references: external, user, local; skip tiny edits; surface only uncertainty/conflict.", + "Write `description` early; route/boundary edits need `trigger_eval.py`; releases need risk-matched gates before folders.", + "Add output-risk, artifact-design, prompt-quality, system-model, next directions only when earned." + ], + "decision_points": [ + "Route by frontmatter `description`.", + "Keep `SKILL.md` lean; put guidance in `references/`, logic in `scripts/`, and evidence in `reports/`.", + "Use the lightest reliable process.", + "`Scaffold`: exploratory/personal. `Production`: team reuse. `Library`: shared infra. `Governed`: high-trust/release-critical.", + "Rules: [Method](references/skill-engineering-method.md), [Operating Modes](references/operating-modes.md), [Resource Boundaries](references/resource-boundaries.md).", + "Ask one focused clarification when the real job, output, or exclusion boundary is unclear.", + "Escalate visible tradeoffs when benchmark patterns conflict with local privacy, naming, or governance constraints.", + "Do not silently broaden the skill into adjacent jobs just because the examples are nearby." + ], + "failure_modes": [ + "Tables can render as dense grids with weak hierarchy or poor mobile readability.", + "Long bullets can make the output look complete while hiding the actual decision logic.", + "Footnote markers or dense citation notes can interrupt the reading flow.", + "Evidence can be over-attached to obvious statements and under-attached to risky claims.", + "Screenshots can be captured from the wrong state, wrong viewport, or wrong crop.", + "Missing screenshots can cause the skill to invent visual references instead of declaring the gap.", + "Users start invoking the skill for adjacent one-off or explanation-only requests.", + "Outputs remain valid but become generic, cluttered, or weakly aligned with the user's domain.", + "Borrowed benchmark patterns no longer fit the local job or add ceremony without payoff.", + "Skill usage becomes team-critical while ownership, review cadence, or rollback evidence stays informal." + ] + }, + "resources": { + "references": [ + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md" + ], + "scripts": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py" + ], + "assets": [ + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "reports": [ + "reports/benchmark_methodology.md", + "reports/intent-context.json", + "reports/intent-confidence.json", + "reports/intent-confidence.md", + "reports/reference-synthesis.json", + "reports/reference-synthesis.md", + "reports/output-risk-profile.json", + "reports/output-risk-profile.md", + "reports/artifact-design-profile.json", + "reports/artifact-design-profile.md", + "reports/prompt-quality-profile.json", + "reports/prompt-quality-profile.md", + "reports/system-model.json", + "reports/system-model.md", + "reports/iteration-directions.json", + "reports/iteration-directions.md", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/output_quality_scorecard.json", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.json", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.json", + "reports/output_blind_review_pack.md", + "reports/output_blind_answer_key.json", + "reports/output_review_adjudication.json", + "reports/output_review_adjudication.md", + "reports/review_annotations.json", + "reports/review_annotations.md", + "reports/conformance_matrix.json", + "reports/conformance_matrix.md", + "reports/security_trust_report.json", + "reports/security_trust_report.md", + "reports/runtime_permission_probes.json", + "reports/runtime_permission_probes.md", + "reports/telemetry_hook_recipes.json", + "reports/telemetry_hook_recipes.md", + "reports/skill_atlas.json", + "reports/skill_atlas.html", + "reports/skill-os-2-review.md", + "reports/portability_score.json", + "reports/portability_score.md", + "reports/governance_score.json" + ] + }, + "eval_plan": { + "trigger": [ + "evals/README.md", + "evals/adaptation/user_signals.example.jsonl", + "evals/adversarial/trigger_cases.json", + "evals/blind_holdout/trigger_cases.json", + "evals/confusion/route_cases.json", + "evals/dev/trigger_cases.json", + "evals/failure-cases.md", + "evals/history/2026-03-31-context-first-pack.json", + "evals/history/2026-03-31-family-suite-expansion.json", + "evals/history/2026-03-31-governance-quality-gates.json", + "evals/history/2026-03-31-governed-assets-and-history.json", + "evals/history/2026-04-01-route-scorecard-foundation.json", + "evals/history/README.md", + "evals/history/description_optimization/2026-03-31-description-optimization-suite.json", + "evals/history/description_optimization/2026-04-01-adversarial-calibration-and-family-drift.json", + "evals/history/description_optimization/2026-04-01-blind-holdout-and-drift-history.json", + "evals/history/description_optimization/2026-04-01-judge-backed-blind-eval.json", + "evals/history/description_optimization/README.md", + "evals/holdout/trigger_cases.json", + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json", + "evals/packaging_expectations.json", + "evals/promotion_policy.md", + "evals/semantic_config.json", + "evals/train/trigger_cases.json", + "evals/trigger_cases.json" + ], + "output": [ + "evals/output/cases.jsonl", + "evals/output/fixtures/release-brief-source.md", + "evals/output/schema.json" + ], + "adversarial": [ + "evals/adversarial/trigger_cases.json" + ], + "baseline": "without_skill" + }, + "risk": { + "output_risk": "high", + "execution_risk": "medium", + "trust_boundary": "external" + }, + "governance": { + "owner": "Yao Team", + "maturity": "governed", + "review_cadence": "quarterly", + "review_due": "" + }, + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "source_files": [ + "SKILL.md", + "manifest.json", + "agents/interface.yaml", + "reports/intent-context.json", + "reports/output-risk-profile.json", + "reports/system-model.json" + ] +} diff --git a/skill-ir/schema.json b/skill-ir/schema.json new file mode 100644 index 0000000..1011d1f --- /dev/null +++ b/skill-ir/schema.json @@ -0,0 +1,156 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.example/skill-ir.schema.json", + "title": "Yao Skill IR", + "description": "Platform-neutral capability contract for Yao Meta Skill 2.0.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "name", + "job_to_be_done", + "trigger_surface", + "workflow", + "resources", + "eval_plan", + "risk", + "governance" + ], + "properties": { + "schema_version": { + "const": "2.0.0" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string" + }, + "job_to_be_done": { + "type": "string", + "minLength": 1 + }, + "trigger_surface": { + "type": "object", + "additionalProperties": false, + "required": ["description", "should_trigger", "should_not_trigger", "edge_cases"], + "properties": { + "description": {"type": "string", "minLength": 1}, + "should_trigger": { + "type": "array", + "items": {"type": "string"} + }, + "should_not_trigger": { + "type": "array", + "items": {"type": "string"} + }, + "edge_cases": { + "type": "array", + "items": {"type": "string"} + } + } + }, + "workflow": { + "type": "object", + "additionalProperties": false, + "required": ["steps", "decision_points", "failure_modes"], + "properties": { + "steps": { + "type": "array", + "items": {"type": "string"} + }, + "decision_points": { + "type": "array", + "items": {"type": "string"} + }, + "failure_modes": { + "type": "array", + "items": {"type": "string"} + } + } + }, + "resources": { + "type": "object", + "additionalProperties": false, + "required": ["references", "scripts", "assets", "reports"], + "properties": { + "references": { + "type": "array", + "items": {"type": "string"} + }, + "scripts": { + "type": "array", + "items": {"type": "string"} + }, + "assets": { + "type": "array", + "items": {"type": "string"} + }, + "reports": { + "type": "array", + "items": {"type": "string"} + } + } + }, + "eval_plan": { + "type": "object", + "additionalProperties": false, + "required": ["trigger", "output", "adversarial", "baseline"], + "properties": { + "trigger": { + "type": "array", + "items": {"type": "string"} + }, + "output": { + "type": "array", + "items": {"type": "string"} + }, + "adversarial": { + "type": "array", + "items": {"type": "string"} + }, + "baseline": { + "type": "string" + } + } + }, + "risk": { + "type": "object", + "additionalProperties": false, + "required": ["output_risk", "execution_risk", "trust_boundary"], + "properties": { + "output_risk": { + "enum": ["low", "medium", "high"] + }, + "execution_risk": { + "enum": ["low", "medium", "high"] + }, + "trust_boundary": { + "enum": ["personal", "team", "external"] + } + } + }, + "governance": { + "type": "object", + "additionalProperties": false, + "required": ["owner", "maturity", "review_cadence", "review_due"], + "properties": { + "owner": {"type": "string"}, + "maturity": { + "enum": ["scaffold", "production", "library", "governed"] + }, + "review_cadence": {"type": "string"}, + "review_due": {"type": "string"} + } + }, + "targets": { + "type": "array", + "items": {"type": "string"} + }, + "source_files": { + "type": "array", + "items": {"type": "string"} + } + } +} diff --git a/skill_atlas/README.md b/skill_atlas/README.md new file mode 100644 index 0000000..4724b02 --- /dev/null +++ b/skill_atlas/README.md @@ -0,0 +1,20 @@ +# Skill Atlas + +Skill Atlas is the Skill OS 2.0 layer for managing a workspace of multiple skills. + +It detects: + +- catalog metadata +- route overlap +- shared resource dependencies +- stale skills +- missing owner / review metadata +- no-route opportunities from failure notes + +Run against a workspace: + +```bash +python3 scripts/build_skill_atlas.py --workspace-root .. --output-dir skill_atlas --report-html reports/skill_atlas.html --report-json reports/skill_atlas.json +``` + +The script is local-first and does not modify scanned skills. diff --git a/skill_atlas/catalog.json b/skill_atlas/catalog.json new file mode 100644 index 0000000..a673151 --- /dev/null +++ b/skill_atlas/catalog.json @@ -0,0 +1,709 @@ +{ + "workspace_root": ".", + "generated_at": "2026-06-13", + "skills": [ + { + "name": "yao-meta-skill", + "path": ".", + "description": "Create/improve/evaluate agent skills from workflows, prompts, SOPs, scripts. Use for migration/release/package, routing, evals/tests, install checks, 优化已有 skill, 补 trigger 评测. Exclude no-skill summary/translation/docs.", + "owner": "Yao Team", + "version": "1.1.0", + "status": "active", + "maturity": "governed", + "updated_at": "2026-03-31", + "review_cadence": "quarterly", + "targets": [ + "openai", + "claude", + "generic", + "agent-skills-compatible", + "vscode" + ], + "resources": [ + "scripts/adaptation_patch_safety.py", + "scripts/adaptation_report_contracts.py", + "scripts/adjudicate_output_review.py", + "scripts/apply_adaptation.py", + "scripts/benchmark_release_lock.py", + "scripts/build_confusion_matrix.py", + "scripts/build_skill_atlas.py", + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/check_update.py", + "scripts/ci_test.py", + "scripts/collect_feedback.py", + "scripts/compile_skill.py", + "scripts/compile_skill_targets.py", + "scripts/context_sizer.py", + "scripts/create_iteration_snapshot.py", + "scripts/cross_packager.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/diff_eval.py", + "scripts/emit_telemetry_event.py", + "scripts/evidence_consistency_artifact_roles.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_phase_queue.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/export_skill_ir.py", + "scripts/github_benchmark_scan.py", + "scripts/governance_check.py", + "scripts/html_rendering.py", + "scripts/import_output_review_decisions.py", + "scripts/import_telemetry_events.py", + "scripts/init_skill.py", + "scripts/judge_blind_eval.py", + "scripts/lint_skill.py", + "scripts/local_output_eval_runner.py", + "scripts/optimize_description.py", + "scripts/output_review_privacy.py", + "scripts/prepare_output_review_kit.py", + "scripts/prepare_world_class_submission_kit.py", + "scripts/probe_runtime_permissions.py", + "scripts/promotion_checker.py", + "scripts/propose_adaptation.py", + "scripts/provider_output_eval_runner.py", + "scripts/python_compat_check.py", + "scripts/reference_synthesis_markdown.py", + "scripts/registry_audit.py", + "scripts/render_adoption_drift_report.py", + "scripts/render_architecture_maintainability.py", + "scripts/render_artifact_design_profile.py", + "scripts/render_baseline_compare.py", + "scripts/render_benchmark_reproducibility.py", + "scripts/render_context_reports.py", + "scripts/render_daily_skillops_report.py", + "scripts/render_description_drift_history.py", + "scripts/render_eval_dashboard.py", + "scripts/render_evidence_consistency.py", + "scripts/render_intent_confidence.py", + "scripts/render_intent_dialogue.py", + "scripts/render_iteration_directions.py", + "scripts/render_iteration_ledger.py", + "scripts/render_output_risk_profile.py", + "scripts/render_portability_report.py", + "scripts/render_prompt_quality_profile.py", + "scripts/render_reference_scan.py", + "scripts/render_reference_synthesis.py", + "scripts/render_regression_history.py", + "scripts/render_review_annotations.py", + "scripts/render_review_studio.py", + "scripts/render_review_viewer.py", + "scripts/render_review_waivers.py", + "scripts/render_skill_interpretation.py", + "scripts/render_skill_os2_audit.py", + "scripts/render_skill_os2_coverage.py", + "scripts/render_skill_overview.py", + "scripts/render_social_preview.py", + "scripts/render_system_model.py", + "scripts/render_telemetry_hook_recipes.py", + "scripts/render_weekly_curator_report.py", + "scripts/render_world_class_claim_guard.py", + "scripts/render_world_class_evidence_intake.py", + "scripts/render_world_class_evidence_ledger.py", + "scripts/render_world_class_evidence_plan.py", + "scripts/render_world_class_operator_runbook.py", + "scripts/render_world_class_preflight.py", + "scripts/render_world_class_submission_review.py", + "scripts/resource_boundary_check.py", + "scripts/review_studio_action_evidence.py", + "scripts/review_studio_actions.py", + "scripts/review_studio_data.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_output_review.py", + "scripts/review_studio_panels.py", + "scripts/review_studio_skillops.py", + "scripts/review_studio_waivers.py", + "scripts/review_studio_world_class.py", + "scripts/review_viewer_data.py", + "scripts/run_conformance_suite.py", + "scripts/run_description_optimization_suite.py", + "scripts/run_eval_suite.py", + "scripts/run_output_eval.py", + "scripts/run_output_execution.py", + "scripts/simulate_install.py", + "scripts/skill_ir_paths.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skillops_opportunity.py", + "scripts/summarize_user_signals.py", + "scripts/sync_local_install.py", + "scripts/telemetry_native_host.py", + "scripts/trigger_eval.py", + "scripts/trust_check.py", + "scripts/trust_check_scripts.py", + "scripts/upgrade_check.py", + "scripts/validate_skill.py", + "scripts/verify_package.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_human_evidence.py", + "scripts/world_class_native_permission_evidence.py", + "scripts/world_class_native_telemetry_evidence.py", + "scripts/world_class_operator_runbook_coordination.py", + "scripts/world_class_phase_queue.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_preflight_markdown.py", + "scripts/world_class_provider_evidence.py", + "scripts/world_class_repair_checklist.py", + "scripts/world_class_source_checks.py", + "scripts/world_class_submission_kit_rendering.py", + "scripts/world_class_submission_matrix.py", + "scripts/yao.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_create_commands.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_runtime.py", + "scripts/yao_cli_telemetry.py", + "references/artifact-design-doctrine.md", + "references/authoring-discipline.md", + "references/autonomous-adaptation.md", + "references/distribution-registry-method.md", + "references/eval-playbook.md", + "references/gate-selection.md", + "references/governance.md", + "references/human-review-template.md", + "references/intent-dialogue.md", + "references/iteration-philosophy.md", + "references/non-skill-decision-tree.md", + "references/operating-modes.md", + "references/output-eval-method.md", + "references/output-quality-risk.md", + "references/output-visual-quality.md", + "references/packaging-contracts.md", + "references/pattern-extraction-doctrine.md", + "references/platform-capability-matrix.md", + "references/prompt-engineering-doctrine.md", + "references/qa-ladder.md", + "references/reference-scan.md", + "references/regression-cause-taxonomy.md", + "references/resource-boundaries.md", + "references/review-studio-method.md", + "references/review-waiver-method.md", + "references/runtime-conformance-method.md", + "references/skill-archetypes.md", + "references/skill-atlas-method.md", + "references/skill-engineering-method.md", + "references/skill-ir-method.md", + "references/skillops-decision-policy.md", + "references/systems-thinking-doctrine.md", + "references/telemetry-drift-method.md", + "references/trust-security-method.md", + "references/user-memory-policy.md", + "assets/review-studio.css", + "assets/review-viewer.css", + "assets/skill-overview.css", + "assets/skill-overview.js", + "templates/basic_skill.md.j2", + "templates/complex_skill.md.j2" + ], + "token_set": [ + "checks", + "docs", + "evals", + "evaluate", + "exclude", + "improve", + "install", + "migration", + "no-skill", + "package", + "prompts", + "release", + "routing", + "scripts", + "sops", + "summary", + "tests", + "translation", + "trigger", + "workflows", + "优化已有", + "评测" + ], + "atlas_scope": "release", + "actionable": true, + "scope_reason": "default release-actionable skill", + "telemetry": { + "report_present": true, + "report": "reports/adoption_drift_report.json", + "risk_band": "low", + "event_count": 1, + "adoption_sample_count": 1, + "adoption_rate": 100.0, + "candidate_count": 0 + } + }, + { + "name": "release-orchestrator", + "path": "examples/complex-release-orchestrator/generated-skill", + "description": "Coordinate software release preparation, rollout readiness, migration notes, rollback planning, stakeholder communication, and final go/no-go release packets. Use when asked to prepare a release packet, review release readiness, or turn scattered launch notes into one reusable release workflow. Do not use for generic project planning, one-off announcement drafting, or high-level release theory.", + "owner": "Yao Team", + "version": "1.0.0", + "status": "active", + "maturity": "production", + "updated_at": "2026-03-31", + "review_cadence": "quarterly", + "targets": [], + "resources": [ + "scripts/build_release_packet.py", + "references/migration-template.md", + "references/release-checklist.md", + "references/risk-matrix.md", + "references/stakeholder-comms.md" + ], + "token_set": [ + "announcement", + "asked", + "communication", + "coordinate", + "do", + "drafting", + "final", + "generic", + "go", + "high-level", + "launch", + "migration", + "no-go", + "not", + "notes", + "one", + "one-off", + "or", + "packet", + "packets", + "planning", + "preparation", + "prepare", + "project", + "readiness", + "release", + "review", + "rollback", + "rollout", + "scattered", + "software", + "stakeholder", + "theory", + "workflow" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/complex-release-orchestrator/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "description": "Review frontend code for accessibility, risky UI security issues, missing states, and common UX regressions. Use when asked to review React, UI, frontend components, forms, a11y, or pre-merge frontend quality.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "a11y", + "accessibility", + "asked", + "code", + "common", + "components", + "forms", + "frontend", + "issues", + "missing", + "or", + "pre-merge", + "quality", + "react", + "regressions", + "review", + "risky", + "security", + "states", + "ui", + "ux" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/final/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "description": "Review frontend code before merge.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "before", + "code", + "frontend", + "merge", + "review" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/v0/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "description": "Review frontend code for accessibility, risky UI security issues, missing loading and error states, and pre-merge regressions. Use when asked to review React, UI, forms, frontend components, or merge-readiness for the frontend.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "accessibility", + "asked", + "code", + "components", + "error", + "forms", + "frontend", + "issues", + "loading", + "merge-readiness", + "missing", + "or", + "pre-merge", + "react", + "regressions", + "review", + "risky", + "security", + "states", + "ui" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/evolution-frontend-review/v1/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill", + "description": "Build governed incident command packets. Use when asked to standardize incident review, run severity assessment, or assemble incident communication.", + "owner": "Incident Operations Team", + "version": "1.0.0", + "status": "active", + "maturity": "governed", + "updated_at": "2026-03-31", + "review_cadence": "monthly", + "targets": [ + "openai", + "claude", + "generic" + ], + "resources": [ + "scripts/build_incident_packet.py", + "references/comms-policy.md", + "references/review-policy.md", + "references/severity-matrix.md" + ], + "token_set": [ + "asked", + "assemble", + "assessment", + "build", + "command", + "communication", + "governed", + "incident", + "or", + "packets", + "review", + "run", + "severity", + "standardize" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/governed-incident-command/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "description": "Clean messy meeting notes into structured markdown summaries. Use when asked to organize meeting notes, extract action items, separate decisions from open questions, or turn rough notes into a clean recap.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "action", + "asked", + "clean", + "decisions", + "extract", + "items", + "markdown", + "meeting", + "messy", + "notes", + "open", + "or", + "organize", + "questions", + "recap", + "rough", + "separate", + "structured", + "summaries" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/simple-note-cleanup/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "description": "Review frontend code for accessibility, UI security, missing states, and UX regressions. Use when asked to review React components, run a pre-merge frontend review, or check a11y and unsafe rendering.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [ + "references/checklist.md" + ], + "token_set": [ + "a11y", + "accessibility", + "asked", + "check", + "code", + "components", + "frontend", + "missing", + "or", + "pre-merge", + "react", + "regressions", + "rendering", + "review", + "run", + "security", + "states", + "ui", + "unsafe", + "ux" + ], + "atlas_scope": "example", + "actionable": false, + "scope_reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package.", + "telemetry": { + "report_present": false, + "report": "examples/team-frontend-review/generated-skill/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "description": "Test fixture for governance validation.", + "owner": "", + "version": "1.0.0", + "status": "frozen", + "maturity": "governed", + "updated_at": "2026/03/31", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "fixture", + "governance", + "test", + "validation" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/governance_invalid_manifest/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "description": "Broken YAML fixture for packager failure testing.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "broken", + "failure", + "fixture", + "packager", + "testing", + "yaml" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/package_invalid_yaml/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "description": "Broken fixture for packager failure testing.", + "owner": "", + "version": "", + "status": "", + "maturity": "", + "updated_at": "", + "review_cadence": "", + "targets": [], + "resources": [], + "token_set": [ + "broken", + "failure", + "fixture", + "packager", + "testing" + ], + "atlas_scope": "test-fixture", + "actionable": false, + "scope_reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers.", + "telemetry": { + "report_present": false, + "report": "tests/fixtures/package_missing_interface_field/reports/adoption_drift_report.json", + "risk_band": "missing", + "event_count": 0, + "adoption_sample_count": 0, + "adoption_rate": 0, + "candidate_count": 0 + } + } + ], + "summary": { + "skill_count": 11, + "actionable_skill_count": 1, + "route_collision_count": 5, + "actionable_route_collision_count": 0, + "owner_gap_count": 8, + "actionable_owner_gap_count": 0, + "stale_count": 9, + "actionable_stale_count": 0, + "shared_resource_count": 0, + "no_route_opportunity_count": 0, + "telemetry_report_count": 1, + "drift_signal_count": 0, + "actionable_drift_signal_count": 0, + "non_actionable_issue_count": 22 + } +} diff --git a/skill_atlas/catalog.schema.json b/skill_atlas/catalog.schema.json new file mode 100644 index 0000000..0005229 --- /dev/null +++ b/skill_atlas/catalog.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://yao.example/skill-atlas-catalog.schema.json", + "title": "Yao Skill Atlas Catalog", + "type": "object", + "additionalProperties": false, + "required": ["workspace_root", "generated_at", "skills", "summary"], + "properties": { + "workspace_root": {"type": "string"}, + "generated_at": {"type": "string"}, + "skills": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "path", "description"], + "additionalProperties": true, + "properties": { + "name": {"type": "string"}, + "path": {"type": "string"}, + "description": {"type": "string"}, + "owner": {"type": "string"}, + "maturity": {"type": "string"}, + "updated_at": {"type": "string"}, + "review_cadence": {"type": "string"} + } + } + }, + "summary": { + "type": "object", + "additionalProperties": true + } + } +} diff --git a/skill_atlas/dependency_graph.json b/skill_atlas/dependency_graph.json new file mode 100644 index 0000000..35682e6 --- /dev/null +++ b/skill_atlas/dependency_graph.json @@ -0,0 +1,49 @@ +{ + "nodes": [ + { + "name": "yao-meta-skill", + "path": "." + }, + { + "name": "release-orchestrator", + "path": "examples/complex-release-orchestrator/generated-skill" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1" + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field" + } + ], + "shared_resources": [] +} diff --git a/skill_atlas/drift_signals.json b/skill_atlas/drift_signals.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/skill_atlas/drift_signals.json @@ -0,0 +1 @@ +[] diff --git a/skill_atlas/no_route_opportunities.json b/skill_atlas/no_route_opportunities.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/skill_atlas/no_route_opportunities.json @@ -0,0 +1 @@ +[] diff --git a/skill_atlas/owner_review_gaps.json b/skill_atlas/owner_review_gaps.json new file mode 100644 index 0000000..351248f --- /dev/null +++ b/skill_atlas/owner_review_gaps.json @@ -0,0 +1,89 @@ +[ + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "example" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "missing": [ + "owner", + "review_cadence" + ], + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "missing": [ + "owner", + "review_cadence", + "maturity" + ], + "actionable": false, + "scope": "test-fixture" + } +] diff --git a/skill_atlas/policy.json b/skill_atlas/policy.json new file mode 100644 index 0000000..f903e58 --- /dev/null +++ b/skill_atlas/policy.json @@ -0,0 +1,23 @@ +{ + "schema_version": "1.0", + "scope_rules": [ + { + "path_prefix": "examples/", + "scope": "example", + "actionable": false, + "reason": "Generated examples and evolution snapshots are portfolio evidence, not release-actionable library skills for the root package." + }, + { + "path_prefix": "tests/fixtures/", + "scope": "test-fixture", + "actionable": false, + "reason": "Fixtures intentionally include invalid or broken skills to verify validators and packagers." + }, + { + "path_prefix": "geo-ranking-article-generator", + "scope": "embedded-generated-skill", + "actionable": false, + "reason": "This nested generated skill is retained as local evidence and should not gate the root Meta Skill release." + } + ] +} diff --git a/skill_atlas/route_overlap_matrix.csv b/skill_atlas/route_overlap_matrix.csv new file mode 100644 index 0000000..577f808 --- /dev/null +++ b/skill_atlas/route_overlap_matrix.csv @@ -0,0 +1,56 @@ +skill_a,skill_b,path_a,path_b,score,status,actionable,scope_a,scope_b +yao-meta-skill,release-orchestrator,.,examples/complex-release-orchestrator/generated-skill,0.037,clear,False,release,example +yao-meta-skill,frontend-review,.,examples/evolution-frontend-review/final,0.0,clear,False,release,example +yao-meta-skill,frontend-review,.,examples/evolution-frontend-review/v0,0.0,clear,False,release,example +yao-meta-skill,frontend-review,.,examples/evolution-frontend-review/v1,0.0,clear,False,release,example +yao-meta-skill,incident-command-governor,.,examples/governed-incident-command/generated-skill,0.0,clear,False,release,example +yao-meta-skill,note-cleanup,.,examples/simple-note-cleanup/generated-skill,0.0,clear,False,release,example +yao-meta-skill,frontend-review,.,examples/team-frontend-review/generated-skill,0.0,clear,False,release,example +yao-meta-skill,invalid-governance-skill,.,tests/fixtures/governance_invalid_manifest,0.0,clear,False,release,test-fixture +yao-meta-skill,broken-yaml-skill,.,tests/fixtures/package_invalid_yaml,0.0,clear,False,release,test-fixture +yao-meta-skill,broken-skill,.,tests/fixtures/package_missing_interface_field,0.0,clear,False,release,test-fixture +release-orchestrator,frontend-review,examples/complex-release-orchestrator/generated-skill,examples/evolution-frontend-review/final,0.058,clear,False,example,example +release-orchestrator,frontend-review,examples/complex-release-orchestrator/generated-skill,examples/evolution-frontend-review/v0,0.026,clear,False,example,example +release-orchestrator,frontend-review,examples/complex-release-orchestrator/generated-skill,examples/evolution-frontend-review/v1,0.059,clear,False,example,example +release-orchestrator,incident-command-governor,examples/complex-release-orchestrator/generated-skill,examples/governed-incident-command/generated-skill,0.116,clear,False,example,example +release-orchestrator,note-cleanup,examples/complex-release-orchestrator/generated-skill,examples/simple-note-cleanup/generated-skill,0.06,clear,False,example,example +release-orchestrator,frontend-review,examples/complex-release-orchestrator/generated-skill,examples/team-frontend-review/generated-skill,0.059,clear,False,example,example +release-orchestrator,invalid-governance-skill,examples/complex-release-orchestrator/generated-skill,tests/fixtures/governance_invalid_manifest,0.0,clear,False,example,test-fixture +release-orchestrator,broken-yaml-skill,examples/complex-release-orchestrator/generated-skill,tests/fixtures/package_invalid_yaml,0.0,clear,False,example,test-fixture +release-orchestrator,broken-skill,examples/complex-release-orchestrator/generated-skill,tests/fixtures/package_missing_interface_field,0.0,clear,False,example,test-fixture +frontend-review,frontend-review,examples/evolution-frontend-review/final,examples/evolution-frontend-review/v0,0.13,clear,False,example,example +frontend-review,frontend-review,examples/evolution-frontend-review/final,examples/evolution-frontend-review/v1,0.708,collision,False,example,example +frontend-review,incident-command-governor,examples/evolution-frontend-review/final,examples/governed-incident-command/generated-skill,0.094,clear,False,example,example +frontend-review,note-cleanup,examples/evolution-frontend-review/final,examples/simple-note-cleanup/generated-skill,0.053,clear,False,example,example +frontend-review,frontend-review,examples/evolution-frontend-review/final,examples/team-frontend-review/generated-skill,0.64,collision,False,example,example +frontend-review,invalid-governance-skill,examples/evolution-frontend-review/final,tests/fixtures/governance_invalid_manifest,0.0,clear,False,example,test-fixture +frontend-review,broken-yaml-skill,examples/evolution-frontend-review/final,tests/fixtures/package_invalid_yaml,0.0,clear,False,example,test-fixture +frontend-review,broken-skill,examples/evolution-frontend-review/final,tests/fixtures/package_missing_interface_field,0.0,clear,False,example,test-fixture +frontend-review,frontend-review,examples/evolution-frontend-review/v0,examples/evolution-frontend-review/v1,0.136,clear,False,example,example +frontend-review,incident-command-governor,examples/evolution-frontend-review/v0,examples/governed-incident-command/generated-skill,0.056,clear,False,example,example +frontend-review,note-cleanup,examples/evolution-frontend-review/v0,examples/simple-note-cleanup/generated-skill,0.0,clear,False,example,example +frontend-review,frontend-review,examples/evolution-frontend-review/v0,examples/team-frontend-review/generated-skill,0.136,clear,False,example,example +frontend-review,invalid-governance-skill,examples/evolution-frontend-review/v0,tests/fixtures/governance_invalid_manifest,0.0,clear,False,example,test-fixture +frontend-review,broken-yaml-skill,examples/evolution-frontend-review/v0,tests/fixtures/package_invalid_yaml,0.0,clear,False,example,test-fixture +frontend-review,broken-skill,examples/evolution-frontend-review/v0,tests/fixtures/package_missing_interface_field,0.0,clear,False,example,test-fixture +frontend-review,incident-command-governor,examples/evolution-frontend-review/v1,examples/governed-incident-command/generated-skill,0.097,clear,False,example,example +frontend-review,note-cleanup,examples/evolution-frontend-review/v1,examples/simple-note-cleanup/generated-skill,0.054,clear,False,example,example +frontend-review,frontend-review,examples/evolution-frontend-review/v1,examples/team-frontend-review/generated-skill,0.538,collision,False,example,example +frontend-review,invalid-governance-skill,examples/evolution-frontend-review/v1,tests/fixtures/governance_invalid_manifest,0.0,clear,False,example,test-fixture +frontend-review,broken-yaml-skill,examples/evolution-frontend-review/v1,tests/fixtures/package_invalid_yaml,0.0,clear,False,example,test-fixture +frontend-review,broken-skill,examples/evolution-frontend-review/v1,tests/fixtures/package_missing_interface_field,0.0,clear,False,example,test-fixture +incident-command-governor,note-cleanup,examples/governed-incident-command/generated-skill,examples/simple-note-cleanup/generated-skill,0.065,clear,False,example,example +incident-command-governor,frontend-review,examples/governed-incident-command/generated-skill,examples/team-frontend-review/generated-skill,0.133,clear,False,example,example +incident-command-governor,invalid-governance-skill,examples/governed-incident-command/generated-skill,tests/fixtures/governance_invalid_manifest,0.0,clear,False,example,test-fixture +incident-command-governor,broken-yaml-skill,examples/governed-incident-command/generated-skill,tests/fixtures/package_invalid_yaml,0.0,clear,False,example,test-fixture +incident-command-governor,broken-skill,examples/governed-incident-command/generated-skill,tests/fixtures/package_missing_interface_field,0.0,clear,False,example,test-fixture +note-cleanup,frontend-review,examples/simple-note-cleanup/generated-skill,examples/team-frontend-review/generated-skill,0.054,clear,False,example,example +note-cleanup,invalid-governance-skill,examples/simple-note-cleanup/generated-skill,tests/fixtures/governance_invalid_manifest,0.0,clear,False,example,test-fixture +note-cleanup,broken-yaml-skill,examples/simple-note-cleanup/generated-skill,tests/fixtures/package_invalid_yaml,0.0,clear,False,example,test-fixture +note-cleanup,broken-skill,examples/simple-note-cleanup/generated-skill,tests/fixtures/package_missing_interface_field,0.0,clear,False,example,test-fixture +frontend-review,invalid-governance-skill,examples/team-frontend-review/generated-skill,tests/fixtures/governance_invalid_manifest,0.0,clear,False,example,test-fixture +frontend-review,broken-yaml-skill,examples/team-frontend-review/generated-skill,tests/fixtures/package_invalid_yaml,0.0,clear,False,example,test-fixture +frontend-review,broken-skill,examples/team-frontend-review/generated-skill,tests/fixtures/package_missing_interface_field,0.0,clear,False,example,test-fixture +invalid-governance-skill,broken-yaml-skill,tests/fixtures/governance_invalid_manifest,tests/fixtures/package_invalid_yaml,0.111,clear,False,test-fixture,test-fixture +invalid-governance-skill,broken-skill,tests/fixtures/governance_invalid_manifest,tests/fixtures/package_missing_interface_field,0.125,clear,False,test-fixture,test-fixture +broken-yaml-skill,broken-skill,tests/fixtures/package_invalid_yaml,tests/fixtures/package_missing_interface_field,0.833,collision,False,test-fixture,test-fixture diff --git a/skill_atlas/stale_skills.json b/skill_atlas/stale_skills.json new file mode 100644 index 0000000..60e061b --- /dev/null +++ b/skill_atlas/stale_skills.json @@ -0,0 +1,67 @@ +[ + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/final", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v0", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/evolution-frontend-review/v1", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "incident-command-governor", + "path": "examples/governed-incident-command/generated-skill", + "reason": "review overdue by cadence monthly", + "age_days": 74, + "allowed_days": 31, + "actionable": false, + "scope": "example" + }, + { + "name": "note-cleanup", + "path": "examples/simple-note-cleanup/generated-skill", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "frontend-review", + "path": "examples/team-frontend-review/generated-skill", + "reason": "missing updated_at", + "actionable": false, + "scope": "example" + }, + { + "name": "invalid-governance-skill", + "path": "tests/fixtures/governance_invalid_manifest", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-yaml-skill", + "path": "tests/fixtures/package_invalid_yaml", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + }, + { + "name": "broken-skill", + "path": "tests/fixtures/package_missing_interface_field", + "reason": "missing updated_at", + "actionable": false, + "scope": "test-fixture" + } +] diff --git a/templates/basic_skill.md.j2 b/templates/basic_skill.md.j2 new file mode 100644 index 0000000..1624f4e --- /dev/null +++ b/templates/basic_skill.md.j2 @@ -0,0 +1,42 @@ +--- +name: {{ skill_name }} +description: {{ description }} +metadata: + author: {{ author }} +--- + +# {{ title }} + +{{ summary }} + +## When To Use This Skill + +{% for item in when_to_use %} +- {{ item }} +{% endfor %} + +## Workflow + +### 1. Understand the request + +{% for item in understand_steps %} +- {{ item }} +{% endfor %} + +### 2. Execute + +{% for item in execute_steps %} +- {{ item }} +{% endfor %} + +### 3. Validate + +{% for item in validate_steps %} +- {{ item }} +{% endfor %} + +## Reference Map + +{% for item in reference_map %} +- {{ item }} +{% endfor %} diff --git a/templates/complex_skill.md.j2 b/templates/complex_skill.md.j2 new file mode 100644 index 0000000..1f1ab99 --- /dev/null +++ b/templates/complex_skill.md.j2 @@ -0,0 +1,61 @@ +--- +name: {{ skill_name }} +description: {{ description }} +metadata: + author: {{ author }} + version: {{ version }} +--- + +# {{ title }} + +{{ summary }} + +## When To Use This Skill + +{% for item in when_to_use %} +- {{ item }} +{% endfor %} + +## Operating Contract + +- Primary output: {{ primary_output }} +- Main tools or systems: {{ tools_summary }} +- Failure tolerance: {{ failure_tolerance }} + +## Workflow + +### 1. Intake + +{% for item in intake_steps %} +- {{ item }} +{% endfor %} + +### 2. Choose the path + +{% for item in routing_steps %} +- {{ item }} +{% endfor %} + +### 3. Execute + +{% for item in execute_steps %} +- {{ item }} +{% endfor %} + +### 4. Validate + +{% for item in validate_steps %} +- {{ item }} +{% endfor %} + +## Bundled Resources + +- Read `references/` when domain-specific detail or long examples are needed. +- Use `scripts/` when deterministic or repetitive steps should not be re-authored in prose. +- Use `assets/` for templates or output artifacts, not instructions. + +## Reference Map + +{% for item in reference_map %} +- {{ item }} +{% endfor %} diff --git a/tests/fixtures/github_benchmark_scan/bundle.json b/tests/fixtures/github_benchmark_scan/bundle.json new file mode 100644 index 0000000..abbe9b9 --- /dev/null +++ b/tests/fixtures/github_benchmark_scan/bundle.json @@ -0,0 +1,39 @@ +{ + "search_items": [ + { + "full_name": "anthropics/skills", + "html_url": "https://github.com/anthropics/skills", + "description": "A collection of composable skills for agent workflows, evaluation, and reusable operating patterns.", + "stargazers_count": 12800, + "topics": ["skills", "agents", "workflow", "evaluation"], + "language": "Markdown", + "fork": false, + "archived": false + }, + { + "full_name": "openai/skills", + "html_url": "https://github.com/openai/skills", + "description": "Reference skills and patterns for creating portable, reusable agent capabilities.", + "stargazers_count": 9400, + "topics": ["skills", "portable", "agent", "toolchain"], + "language": "Markdown", + "fork": false, + "archived": false + }, + { + "full_name": "microsoft/autogen", + "html_url": "https://github.com/microsoft/autogen", + "description": "Agent orchestration framework with workflow patterns, evaluation, tooling, and portability guidance.", + "stargazers_count": 35500, + "topics": ["agents", "automation", "toolchain", "workflow"], + "language": "Python", + "fork": false, + "archived": false + } + ], + "readmes": { + "anthropics/skills": "# Skills\n\nThis repository documents workflow patterns, evaluation loops, benchmark practices, and review steps for reusable agent skills. It includes examples, references, and quality checks for operating a repeatable process.\n", + "openai/skills": "# Skills\n\nThis project emphasizes portable metadata, compatibility adapters, references, templates, and clear execution entrypoints. It keeps core semantics stable while allowing environment-specific packaging.\n", + "microsoft/autogen": "# AutoGen\n\nThis framework provides toolchain commands, automation flows, orchestration guidance, and validation practices. It highlights scripts, command entrypoints, and reusable execution structures for agent workflows.\n" + } +} diff --git a/tests/fixtures/governance_invalid_manifest/SKILL.md b/tests/fixtures/governance_invalid_manifest/SKILL.md new file mode 100644 index 0000000..7f2c572 --- /dev/null +++ b/tests/fixtures/governance_invalid_manifest/SKILL.md @@ -0,0 +1,8 @@ +--- +name: invalid-governance-skill +description: Test fixture for governance validation. +--- + +# Invalid Governance Skill + +This fixture exists only to verify governance failures. diff --git a/tests/fixtures/governance_invalid_manifest/manifest.json b/tests/fixtures/governance_invalid_manifest/manifest.json new file mode 100644 index 0000000..fc47924 --- /dev/null +++ b/tests/fixtures/governance_invalid_manifest/manifest.json @@ -0,0 +1,8 @@ +{ + "name": "wrong-name", + "version": "1.0.0", + "updated_at": "2026/03/31", + "status": "frozen", + "maturity_tier": "governed", + "lifecycle_stage": "library" +} diff --git a/tests/fixtures/package_invalid_yaml/SKILL.md b/tests/fixtures/package_invalid_yaml/SKILL.md new file mode 100644 index 0000000..1c42887 --- /dev/null +++ b/tests/fixtures/package_invalid_yaml/SKILL.md @@ -0,0 +1,6 @@ +--- +name: broken-yaml-skill +description: Broken YAML fixture for packager failure testing. +--- + +# Broken YAML Skill diff --git a/tests/fixtures/package_invalid_yaml/agents/interface.yaml b/tests/fixtures/package_invalid_yaml/agents/interface.yaml new file mode 100644 index 0000000..c1f52b6 --- /dev/null +++ b/tests/fixtures/package_invalid_yaml/agents/interface.yaml @@ -0,0 +1,8 @@ +interface: + display_name: "Broken YAML" + short_description: "This fixture contains invalid yaml" + default_prompt: "Use this fixture" +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "openai diff --git a/tests/fixtures/package_missing_interface_field/SKILL.md b/tests/fixtures/package_missing_interface_field/SKILL.md new file mode 100644 index 0000000..282c71e --- /dev/null +++ b/tests/fixtures/package_missing_interface_field/SKILL.md @@ -0,0 +1,6 @@ +--- +name: broken-skill +description: Broken fixture for packager failure testing. +--- + +# Broken Skill diff --git a/tests/fixtures/package_missing_interface_field/agents/interface.yaml b/tests/fixtures/package_missing_interface_field/agents/interface.yaml new file mode 100644 index 0000000..122311d --- /dev/null +++ b/tests/fixtures/package_missing_interface_field/agents/interface.yaml @@ -0,0 +1,19 @@ +interface: + display_name: "Broken Skill" + short_description: "Missing default prompt on purpose" +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "openai" + activation: + mode: "manual" + paths: [] + execution: + context: "inline" + shell: "bash" + trust: + source_tier: "local" + remote_inline_execution: "forbid" + remote_metadata_policy: "allow-metadata-only" + degradation: + openai: "metadata-adapter" diff --git a/tests/review_studio_helpers.py b/tests/review_studio_helpers.py new file mode 100644 index 0000000..be697f5 --- /dev/null +++ b/tests/review_studio_helpers.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPTS = ROOT / "scripts" +SCRIPT = SCRIPTS / "render_review_studio.py" + + +def run(args: list[object], allowed_returncodes: tuple[int, ...] = (0,)) -> subprocess.CompletedProcess[str]: + proc = subprocess.run([str(item) for item in args], cwd=ROOT, check=False, capture_output=True, text=True) + if proc.returncode not in allowed_returncodes: + raise subprocess.CalledProcessError(proc.returncode, proc.args, output=proc.stdout, stderr=proc.stderr) + return proc + + +def run_script( + script_name: str, + *args: object, + allowed_returncodes: tuple[int, ...] = (0,), +) -> subprocess.CompletedProcess[str]: + return run([sys.executable, SCRIPTS / script_name, *args], allowed_returncodes=allowed_returncodes) + + +def prepare_tmp_root() -> Path: + tmp_root = ROOT / "tests" / "tmp_review_studio" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + return tmp_root + + +def copy_tmp_report(tmp_root: Path, stem: str) -> None: + for suffix in ("json", "md"): + (ROOT / "reports" / f"{stem}.{suffix}").write_text( + (tmp_root / f"{stem}.{suffix}").read_text(encoding="utf-8"), + encoding="utf-8", + ) + + +def output_execution_has_provider_evidence() -> bool: + path = ROOT / "reports" / "output_execution_runs.json" + if not path.exists(): + return False + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return False + summary = payload.get("summary", {}) + if not isinstance(summary, dict): + return False + return ( + int(summary.get("model_executed_count") or 0) > 0 + and int(summary.get("timing_observed_count") or 0) > 0 + and int(summary.get("token_observed_count") or 0) > 0 + ) + + +def prepare_review_studio_inputs(tmp_root: Path) -> None: + run_script("run_output_eval.py") + run_script("prepare_output_review_kit.py") + if not output_execution_has_provider_evidence(): + run_script("run_output_execution.py", "--runner-command", json.dumps(["python3", "scripts/local_output_eval_runner.py"])) + run_script("adjudicate_output_review.py") + run_script("compile_skill.py", ROOT, "--generated-at", "2026-06-13") + + package_dir = tmp_root / "dist" + run_script( + "cross_packager.py", + ROOT, + "--platform", + "openai", + "--platform", + "claude", + "--platform", + "generic", + "--platform", + "vscode", + "--expectations", + ROOT / "evals" / "packaging_expectations.json", + "--output-dir", + package_dir, + "--zip", + ) + run_script( + "simulate_install.py", + ROOT, + "--package-dir", + package_dir, + "--install-root", + tmp_root / "install-root", + "--output-json", + tmp_root / "install_simulation.json", + "--output-md", + tmp_root / "install_simulation.md", + "--generated-at", + "2026-06-13", + ) + copy_tmp_report(tmp_root, "install_simulation") + + run_script("registry_audit.py", ROOT, "--generated-at", "2026-06-13") + run_script("render_intent_confidence.py", ROOT) + run_script("trust_check.py", ROOT) + run_script("python_compat_check.py", ROOT, "--generated-at", "2026-06-13") + run_script("render_architecture_maintainability.py", ROOT, "--generated-at", "2026-06-13") + run_script("render_context_reports.py") + run_script( + "render_adoption_drift_report.py", + ROOT, + "--events-jsonl", + tmp_root / "telemetry_events.jsonl", + "--record-event", + "skill_activation", + "--activation-type", + "explicit", + "--outcome", + "accepted", + "--timestamp", + "2026-06-13T10:00:00Z", + ) + run_script( + "build_skill_atlas.py", + "--workspace-root", + ROOT, + "--output-dir", + ROOT / "skill_atlas", + "--report-html", + ROOT / "reports" / "skill_atlas.html", + "--report-json", + ROOT / "reports" / "skill_atlas.json", + "--today", + "2026-06-13", + ) + run_script( + "upgrade_check.py", + ROOT, + "--previous-package-json", + ROOT / "registry" / "examples" / "yao-meta-skill-1.0.0.json", + "--current-package-json", + ROOT / "reports" / "registry_audit.json", + "--output-json", + tmp_root / "upgrade_check.json", + "--output-md", + tmp_root / "upgrade_check.md", + "--generated-at", + "2026-06-13", + ) + copy_tmp_report(tmp_root, "upgrade_check") + + run_script("render_review_waivers.py", ROOT, "--generated-at", "2026-06-13") + run_script( + "render_review_annotations.py", + ROOT, + "--annotations-json", + tmp_root / "empty_review_annotations_input.json", + ) + run_script("probe_runtime_permissions.py", ROOT, "--package-dir", package_dir) + + for script_name in [ + "render_skill_os2_audit.py", + "render_world_class_evidence_plan.py", + "render_world_class_evidence_ledger.py", + "render_world_class_evidence_intake.py", + "render_world_class_preflight.py", + "render_world_class_submission_review.py", + "render_world_class_operator_runbook.py", + "render_world_class_claim_guard.py", + "render_skill_os2_coverage.py", + "render_benchmark_reproducibility.py", + ]: + allowed_returncodes = (0, 2) if script_name == "render_world_class_submission_review.py" else (0,) + run_script(script_name, ROOT, "--generated-at", "2026-06-13", allowed_returncodes=allowed_returncodes) + + +def render_review_studio_fixture(tmp_root: Path) -> tuple[Path, Path, subprocess.CompletedProcess[str]]: + prepare_review_studio_inputs(tmp_root) + output_html = tmp_root / "review-studio.html" + output_json = tmp_root / "review-studio.json" + proc = run( + [ + sys.executable, + SCRIPT, + ROOT, + "--output-html", + output_html, + "--output-json", + output_json, + ] + ) + return output_html, output_json, proc diff --git a/tests/review_studio_world_class_assertions.py b/tests/review_studio_world_class_assertions.py new file mode 100644 index 0000000..c7f84be --- /dev/null +++ b/tests/review_studio_world_class_assertions.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""Focused assertions for Review Studio world-class evidence actions.""" + + +def assert_world_class_action(full_payload: dict) -> None: + world_class_action = next( + item for item in full_payload["review_actions"] if item["gate_key"] == "world-class-evidence" + ) + assert {item["path"] for item in world_class_action["source_refs"]} >= { + "reports/world_class_evidence_ledger.md", + "reports/world_class_evidence_plan.md", + "reports/world_class_evidence_intake.md", + "reports/world_class_evidence_preflight.md", + "reports/world_class_evidence_preflight.html", + "reports/world_class_submission_review.md", + "reports/world_class_claim_guard.md", + "evidence/world_class/intake.schema.json", + "evidence/world_class/templates/provider-holdout.intake.json", + "evidence/world_class/templates/human-adjudication.intake.json", + "evidence/world_class/templates/native-permission-enforcement.intake.json", + "evidence/world_class/templates/native-client-telemetry.intake.json", + "reports/skill_os2_audit.md", + }, world_class_action + assert all(item["exists"] for item in world_class_action["source_refs"]), world_class_action + assert all("matched_pattern" in item for item in world_class_action["source_refs"]), world_class_action + assert all("excerpt" in item for item in world_class_action["source_refs"]), world_class_action + assert all(isinstance(item["line"], int) and item["line"] >= 1 for item in world_class_action["source_refs"]), world_class_action + assert any( + item["path"] == "reports/world_class_evidence_ledger.md" + and item["matched_pattern"] == "# World-Class Evidence Ledger" + and "World-Class Evidence Ledger" in item["excerpt"] + for item in world_class_action["source_refs"] + ), world_class_action + assert "world-class-runbook" in world_class_action["verification_command"], world_class_action + assert "--submissions-dir evidence/world_class/submissions" in world_class_action["verification_command"], world_class_action + assert "reports/world_class_operator_runbook.html" in world_class_action["source_fix"], world_class_action + assert {item["key"] for item in world_class_action["evidence_steps"]} == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, world_class_action + + provider_action_step = next( + item for item in world_class_action["evidence_steps"] if item["key"] == "provider-holdout" + ) + assert provider_action_step["status"] == "pending", provider_action_step + provider_readiness = provider_action_step["readiness"] + assert provider_readiness in {"fix-submission", "awaiting-submission"}, provider_action_step + assert provider_action_step["submission_path"] == "evidence/world_class/submissions/provider-holdout.json", provider_action_step + assert provider_action_step["template_path"] == "evidence/world_class/templates/provider-holdout.intake.json", provider_action_step + assert provider_action_step["source_blocked_count"] == 0, provider_action_step + assert provider_action_step["repair_blocked_count"] == 1, provider_action_step + assert provider_action_step["repair_counts_as_completion"] is False, provider_action_step + assert provider_action_step["phase_queue_blocked_count"] == 1, provider_action_step + assert provider_action_step["phase_queue_counts_as_completion"] is False, provider_action_step + provider_phase_queue = {item["phase"]: item for item in provider_action_step["phase_queue"]} + assert set(provider_phase_queue) == {"unblock-access"}, provider_phase_queue + assert provider_phase_queue["unblock-access"]["next_action_id"] == "provider-holdout-precheck-provider-api-key", provider_phase_queue + assert provider_phase_queue["unblock-access"]["row_count"] == 1, provider_phase_queue + assert "operator with provider credentials" in provider_phase_queue["unblock-access"]["owners"], provider_phase_queue + assert provider_action_step["blocked_checks"] == [], provider_action_step + provider_repair_rows = {item["target"]: item for item in provider_action_step["repair_rows"]} + assert set(provider_repair_rows) == {"provider-api-key"}, provider_repair_rows + assert provider_repair_rows["provider-api-key"]["action_id"] == "provider-holdout-precheck-provider-api-key", provider_repair_rows + assert provider_repair_rows["provider-api-key"]["repair_type"] == "precheck", provider_repair_rows + assert provider_repair_rows["provider-api-key"]["phase"] == "unblock-access", provider_repair_rows + assert provider_repair_rows["provider-api-key"]["priority"] == 20, provider_repair_rows + assert provider_repair_rows["provider-api-key"]["owner"] == "operator with provider credentials", provider_repair_rows + assert all(item["counts_as_completion"] is False for item in provider_action_step["repair_rows"]), provider_action_step + assert any("world-class-intake" in item["command"] for item in provider_action_step["commands"]), provider_action_step + assert any("world-class-ledger" in item["command"] for item in provider_action_step["commands"]), provider_action_step + assert any("output-exec --provider-runner openai" in item for item in provider_action_step["runbook"]), provider_action_step + assert not any("" in item or "OPENAI_API_KEY=" in item for item in provider_action_step["runbook"]), provider_action_step + assert "provider-backed model run" in provider_action_step["provenance_requirements"], provider_action_step + assert "reports/output_execution_runs.json summary.model_executed_count > 0" in provider_action_step["success_checks"], provider_action_step + assert "reports/output_execution_runs.json" in provider_action_step["evidence_artifacts"], provider_action_step + provider_role_contract = provider_action_step["artifact_role_contract"] + assert provider_role_contract["role_source"] == "world-class-submission-kit", provider_role_contract + assert provider_role_contract["counts_as_evidence"] is False, provider_role_contract + assert provider_role_contract["artifact_prefill_counts_as_evidence"] is False, provider_role_contract + assert provider_role_contract["submission_ref_total_count"] == 1, provider_role_contract + assert provider_role_contract["submission_ref_ready_count"] == 1, provider_role_contract + provider_role_rows = {item["role"]: item for item in provider_role_contract["roles"]} + assert provider_role_rows["submission-ref"]["copy_to_artifact_refs"] is True, provider_role_rows + assert provider_role_rows["supporting-evidence"]["copy_to_artifact_refs"] is False, provider_role_rows + assert any("provider credentials" in item for item in provider_action_step["privacy_contract"]), provider_action_step + + human_action_step = next( + item for item in world_class_action["evidence_steps"] if item["key"] == "human-adjudication" + ) + assert human_action_step["repair_blocked_count"] >= 2, human_action_step + assert human_action_step["phase_queue_blocked_count"] == 2, human_action_step + human_repair_rows = {item["target"]: item for item in human_action_step["repair_rows"]} + assert human_repair_rows["human-reviewer"]["repair_type"] == "precheck", human_repair_rows + assert human_repair_rows["human-reviewer"]["owner"] == "human reviewer", human_repair_rows + assert human_repair_rows["pending_count"]["repair_type"] == "source-check", human_repair_rows + assert "output-review" in human_repair_rows["pending_count"]["verification_command"], human_repair_rows + assert "prompt_sha256" in " ".join(human_action_step["success_checks"]), human_action_step + assert "prompt_sha256" in " ".join(human_action_step["privacy_contract"]), human_action_step + + expected_awaiting_count = 3 if provider_readiness == "fix-submission" else 4 + expected_invalid_count = 1 if provider_readiness == "fix-submission" else 0 + expected_intake_decision = "fix-intake" if provider_readiness == "fix-submission" else "awaiting-submissions" + expected_review_decision = "fix-submissions" if provider_readiness == "fix-submission" else "awaiting-submissions" + assert full_payload["data"]["world_class_evidence_ledger"]["summary"]["pending_count"] == 4 + assert full_payload["data"]["world_class_evidence_intake"]["summary"]["decision"] == expected_intake_decision + submission_review = full_payload["data"]["world_class_submission_review"] + assert submission_review["summary"]["decision"] == expected_review_decision, submission_review + assert submission_review["summary"]["review_counts_submission_as_completion"] is False, submission_review + assert submission_review["summary"]["awaiting_submission_count"] == expected_awaiting_count, submission_review + assert submission_review["summary"]["invalid_submission_count"] == expected_invalid_count, submission_review + assert submission_review["summary"]["source_check_count"] >= 13, submission_review + assert submission_review["summary"]["source_blocked_count"] >= 6, submission_review + human_review_item = next(item for item in submission_review["items"] if item["evidence_key"] == "human-adjudication") + human_review_source = {item["field"]: item for item in human_review_item["source_checklist"]} + assert human_review_item["observed_state"]["raw_content_allowed"] is False, human_review_item + assert human_review_item["observed_state"]["raw_content_path_count"] == 0, human_review_item + assert human_review_source["raw_content_allowed"]["status"] == "pass", human_review_source + + runbook = full_payload["data"]["world_class_operator_runbook"] + assert runbook["summary"]["decision"] == "collect-evidence", runbook + assert runbook["summary"]["runbook_counts_as_completion"] is False, runbook + assert runbook["summary"]["awaiting_submission_count"] == expected_awaiting_count, runbook + assert runbook["summary"]["invalid_submission_count"] == expected_invalid_count, runbook + intake = full_payload["data"]["world_class_evidence_intake"] + assert intake["summary"]["template_pass_count"] == 4, intake + assert intake["summary"]["operator_checklist_count"] == 4, intake + assert intake["summary"]["operator_checklist_ready_count"] == 0, intake + assert intake["summary"]["ready_to_claim_world_class"] is False, intake + provider_checklist = next(item for item in intake["operator_checklist"] if item["evidence_key"] == "provider-holdout") + assert provider_checklist["readiness"] == provider_readiness, provider_checklist + if provider_readiness == "fix-submission": + assert provider_checklist["submission_status"] == "fail", provider_checklist + else: + assert provider_checklist["submission_status"] in {"missing", "not-found", "not-submitted"}, provider_checklist + assert provider_checklist["source_accepted"] is True, provider_checklist + assert provider_checklist["submission_path"] == "evidence/world_class/submissions/provider-holdout.json", provider_checklist + assert provider_checklist["commands"]["validate_intake"] == "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions", provider_checklist + assert provider_checklist["commands"]["submission_review"] == "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions", provider_checklist + assert provider_checklist["commands"]["refresh_ledger"] == "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions", provider_checklist + assert "provider-backed model run" in provider_checklist["must_collect"]["provenance_requirements"], provider_checklist + assert any( + "output-exec --provider-runner openai" in step for step in provider_checklist["must_collect"]["runbook"] + ), provider_checklist + assert full_payload["data"]["world_class_claim_guard"]["summary"]["decision"] == "claim-guard-pass-evidence-pending" + assert full_payload["data"]["world_class_claim_guard"]["summary"]["violation_count"] == 0 + assert full_payload["data"]["world_class_claim_guard"]["summary"]["ledger_pending_count"] == 4 diff --git a/tests/snapshots/claude_adapter.json b/tests/snapshots/claude_adapter.json new file mode 100644 index 0000000..5bf0ec1 --- /dev/null +++ b/tests/snapshots/claude_adapter.json @@ -0,0 +1,26 @@ +{ + "platform": "claude", + "canonical_metadata": "agents/interface.yaml", + "required_generated_file": "targets/claude/README.md", + "required_fields": [ + "canonical_format", + "activation_mode", + "execution_context", + "shell", + "trust_level", + "remote_inline_execution", + "degradation_strategy", + "portability_profile", + "job_to_be_done", + "ir_source", + "ir_schema_version", + "semantic_contract", + "semantic_parity", + "compiler", + "compiled_contract", + "permission_contract", + "target_permission_contract", + "target_native_contract", + "target_transform" + ] +} diff --git a/tests/snapshots/generic_adapter.json b/tests/snapshots/generic_adapter.json new file mode 100644 index 0000000..542d8b6 --- /dev/null +++ b/tests/snapshots/generic_adapter.json @@ -0,0 +1,26 @@ +{ + "platform": "generic", + "canonical_metadata": "agents/interface.yaml", + "required_generated_file": "targets/generic/adapter.json", + "required_fields": [ + "canonical_format", + "activation_mode", + "execution_context", + "shell", + "trust_level", + "remote_inline_execution", + "degradation_strategy", + "portability_profile", + "job_to_be_done", + "ir_source", + "ir_schema_version", + "semantic_contract", + "semantic_parity", + "compiler", + "compiled_contract", + "permission_contract", + "target_permission_contract", + "target_native_contract", + "target_transform" + ] +} diff --git a/tests/snapshots/openai_adapter.json b/tests/snapshots/openai_adapter.json new file mode 100644 index 0000000..8d63916 --- /dev/null +++ b/tests/snapshots/openai_adapter.json @@ -0,0 +1,26 @@ +{ + "platform": "openai", + "canonical_metadata": "agents/interface.yaml", + "required_generated_file": "targets/openai/agents/openai.yaml", + "required_fields": [ + "canonical_format", + "activation_mode", + "execution_context", + "shell", + "trust_level", + "remote_inline_execution", + "degradation_strategy", + "portability_profile", + "job_to_be_done", + "ir_source", + "ir_schema_version", + "semantic_contract", + "semantic_parity", + "compiler", + "compiled_contract", + "permission_contract", + "target_permission_contract", + "target_native_contract", + "target_transform" + ] +} diff --git a/tests/snapshots/vscode_adapter.json b/tests/snapshots/vscode_adapter.json new file mode 100644 index 0000000..a72867e --- /dev/null +++ b/tests/snapshots/vscode_adapter.json @@ -0,0 +1,26 @@ +{ + "platform": "vscode", + "canonical_metadata": "agents/interface.yaml", + "required_generated_file": "targets/vscode/README.md", + "required_fields": [ + "canonical_format", + "activation_mode", + "execution_context", + "shell", + "trust_level", + "remote_inline_execution", + "degradation_strategy", + "portability_profile", + "job_to_be_done", + "ir_source", + "ir_schema_version", + "semantic_contract", + "semantic_parity", + "compiler", + "compiled_contract", + "permission_contract", + "target_permission_contract", + "target_native_contract", + "target_transform" + ] +} diff --git a/tests/verify_adaptation_safety.py b/tests/verify_adaptation_safety.py new file mode 100644 index 0000000..b6b337e --- /dev/null +++ b/tests/verify_adaptation_safety.py @@ -0,0 +1,460 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +import hashlib +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" +SCAN_SCRIPT = ROOT / "scripts" / "summarize_user_signals.py" +PROPOSE_SCRIPT = ROOT / "scripts" / "propose_adaptation.py" +APPLY_SCRIPT = ROOT / "scripts" / "apply_adaptation.py" +TMP = ROOT / "tests" / "tmp_adaptation_safety" + + +def run_script(*args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + + +def sha256_text(value: str) -> str: + return hashlib.sha256(value.encode("utf-8")).hexdigest() + + +def assert_report_contract_mirrors(payload: dict, contract_key: str) -> None: + contract = payload["report_contract"] + assert contract["top_level_mirrors_summary"] is True, payload + assert contract[f"top_level_mirrors_{contract_key}"] is True, payload + for field in contract["summary_fields"]: + assert payload[field] == payload["summary"][field], (field, payload.get(field), payload["summary"].get(field)) + for field in contract[f"{contract_key}_fields"]: + assert payload[field] == payload[contract_key][field], (field, payload.get(field), payload[contract_key].get(field)) + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + skill_dir = TMP / "adaptive-demo-skill" + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + source = TMP / "curated-user-signals.jsonl" + source.write_text( + "\n".join( + [ + json.dumps({"text": "报告默认中文简体,同时右上角提供英文切换。"}, ensure_ascii=False), + json.dumps({"message": "新的 HTML 报告要双语,但默认中文简体。"}, ensure_ascii=False), + json.dumps({"content": "报告 UI 需要 Kami 白底排版、图表模块和清晰导航。"}, ensure_ascii=False), + json.dumps({"excerpt": "HTML 报告还是白底 Kami 风格,图表不要挤在一起。"}, ensure_ascii=False), + json.dumps({"note": "不要自动扫描私人日志;必须由用户提供明确路径。"}, ensure_ascii=False), + json.dumps({"body": "自适应升级需要先输出提案,授权后再修改,并能回滚。"}, ensure_ascii=False), + json.dumps({"text": "隐私证据里也要保护 token=abc123456789、sk-1234567890abcdef 和 /Users/laoyao/private/path。"}, ensure_ascii=False), + json.dumps({"text": "PDF 只提过一次,不能当作稳定偏好。"}, ensure_ascii=False), + ] + ) + + "\n", + encoding="utf-8", + ) + + scan_proc = run_script( + str(SCAN_SCRIPT), + str(skill_dir), + "--source", + str(source), + "--generated-at", + "2026-06-15T00:00:00Z", + ) + assert scan_proc.returncode == 0, scan_proc.stderr + scan_payload = json.loads(scan_proc.stdout) + assert scan_payload["ok"], scan_payload + assert scan_payload["privacy_contract"]["local_only"] is True, scan_payload + assert scan_payload["privacy_contract"]["implicit_private_log_scan"] is False, scan_payload + assert scan_payload["privacy_contract"]["raw_content_stored"] is False, scan_payload + assert scan_payload["privacy_contract"]["writes_repository_files"] is False, scan_payload + assert scan_payload["source"]["path"].startswith("[external-explicit-source]/"), scan_payload + pattern_ids = {item["pattern_id"] for item in scan_payload["patterns"]} + assert {"language_default", "report_ui", "approval_safety"} <= pattern_ids, scan_payload + serialized = json.dumps(scan_payload, ensure_ascii=False) + assert "sk-1234567890abcdef" not in serialized, serialized + assert "token=abc123456789" not in serialized, serialized + assert "/Users/laoyao/private" not in serialized, serialized + assert "[REDACTED_SECRET]" in serialized, serialized + assert "[LOCAL_PATH]" in serialized, serialized + assert (reports_dir / "user_patterns.json").exists(), reports_dir + assert (reports_dir / "user_patterns.md").exists(), reports_dir + + propose_proc = run_script( + str(PROPOSE_SCRIPT), + str(skill_dir), + "--generated-at", + "2026-06-15T00:00:00Z", + ) + assert propose_proc.returncode == 0, propose_proc.stderr + proposal_payload = json.loads(propose_proc.stdout) + assert proposal_payload["ok"], proposal_payload + assert_report_contract_mirrors(proposal_payload, "proposal_contract") + assert proposal_payload["summary"]["apply_supported"] is True, proposal_payload + assert proposal_payload["proposal_count"] == proposal_payload["summary"]["proposal_count"], proposal_payload + assert proposal_payload["approval_draft_supported"] is True, proposal_payload + assert proposal_payload["proposal_contract"]["proposal_only"] is True, proposal_payload + assert proposal_payload["proposal_contract"]["writes_repository_files"] is False, proposal_payload + assert proposal_payload["proposal_contract"]["apply_command_available"] is True, proposal_payload + assert proposal_payload["proposal_contract"]["target_file_sha256_required_for_apply"] is True, proposal_payload + assert proposal_payload["proposal_contract"]["approval_draft_supported"] is True, proposal_payload + assert proposal_payload["summary"]["proposal_count"] >= 3, proposal_payload + assert all(item["status"] == "proposal-only" for item in proposal_payload["proposals"]), proposal_payload + assert all(item["requires_approval"] is True for item in proposal_payload["proposals"]), proposal_payload + assert all(item["write_allowed_without_approval"] is False for item in proposal_payload["proposals"]), proposal_payload + assert any( + any("tests/verify_adaptation_safety.py" in command for command in item["verification_commands"]) + for item in proposal_payload["proposals"] + ), proposal_payload + assert (reports_dir / "adaptation_proposals.json").exists(), reports_dir + assert (reports_dir / "adaptation_proposals.md").exists(), reports_dir + + template_proc = run_script(str(APPLY_SCRIPT), str(skill_dir), "--write-template", "--generated-at", "2026-06-15T00:00:00Z") + assert template_proc.returncode == 0, template_proc.stderr + template_payload = json.loads(template_proc.stdout) + assert_report_contract_mirrors(template_payload, "apply_contract") + assert template_payload["summary"]["apply_supported"] is True, template_payload + assert template_payload["apply_supported"] is True, template_payload + assert template_payload["summary"]["attempt_count"] == 0, template_payload + assert template_payload["apply_contract"]["target_file_sha256_required"] is True, template_payload + assert template_payload["apply_contract"]["approval_draft_supported"] is True, template_payload + assert (reports_dir / "adaptation_approval_ledger.json").exists(), reports_dir + assert (reports_dir / "adaptation_regression_report.json").exists(), reports_dir + template_ledger = json.loads((reports_dir / "adaptation_approval_ledger.json").read_text(encoding="utf-8")) + assert_report_contract_mirrors(template_ledger, "approval_contract") + assert template_ledger["pending_review_count"] == 0, template_ledger + + missing_source_proc = run_script(str(SCAN_SCRIPT), str(skill_dir)) + assert missing_source_proc.returncode != 0, missing_source_proc + + history_source = TMP / ".zsh_history" + history_source.write_text("报告默认中文\n报告默认中文\n", encoding="utf-8") + history_proc = run_script(str(SCAN_SCRIPT), str(skill_dir), "--source", str(history_source)) + assert history_proc.returncode == 2, history_proc.stdout + history_payload = json.loads(history_proc.stdout) + assert history_payload["ok"] is False, history_payload + assert any("Refusing private history source" in item for item in history_payload["failures"]), history_payload + + cli_skill_dir = TMP / "cli-adaptive-demo-skill" + cli_skill_dir.mkdir(parents=True, exist_ok=True) + cli_source = TMP / "cli-user-signals.jsonl" + cli_source.write_text( + "\n".join( + [ + json.dumps({"text": "报告默认中文简体,同时提供英文切换。"}, ensure_ascii=False), + json.dumps({"text": "新的 HTML 报告还是默认中文简体,并保留英文版。"}, ensure_ascii=False), + json.dumps({"text": "报告 UI 要保持 Kami 白底排版和图表模块。"}, ensure_ascii=False), + json.dumps({"text": "HTML 报告的图表和白底 Kami 排版都要清晰。"}, ensure_ascii=False), + json.dumps({"text": "自适应升级必须先生成提案,授权后再修改。"}, ensure_ascii=False), + json.dumps({"text": "不要默认扫描私人日志,要由用户提供明确路径。"}, ensure_ascii=False), + ] + ) + + "\n", + encoding="utf-8", + ) + cli_scan = run_script( + str(CLI), + "adapt-scan", + str(cli_skill_dir), + "--source", + str(cli_source), + "--generated-at", + "2026-06-15T00:00:00Z", + ) + assert cli_scan.returncode == 0, cli_scan.stderr + cli_scan_payload = json.loads(cli_scan.stdout) + assert cli_scan_payload["summary"]["pattern_count"] >= 3, cli_scan_payload + assert cli_scan_payload["privacy_contract"]["writes_repository_files"] is False, cli_scan_payload + assert (cli_skill_dir / "reports" / "user_patterns.json").exists(), cli_skill_dir + cli_propose = run_script( + str(CLI), + "adapt-propose", + str(cli_skill_dir), + "--generated-at", + "2026-06-15T00:00:00Z", + ) + assert cli_propose.returncode == 0, cli_propose.stderr + cli_proposal_payload = json.loads(cli_propose.stdout) + assert_report_contract_mirrors(cli_proposal_payload, "proposal_contract") + assert cli_proposal_payload["summary"]["proposal_count"] >= 3, cli_proposal_payload + assert cli_proposal_payload["proposal_count"] == cli_proposal_payload["summary"]["proposal_count"], cli_proposal_payload + assert cli_proposal_payload["summary"]["apply_supported"] is True, cli_proposal_payload + assert cli_proposal_payload["proposal_contract"]["proposal_only"] is True, cli_proposal_payload + assert cli_proposal_payload["proposal_contract"]["writes_repository_files"] is False, cli_proposal_payload + assert cli_proposal_payload["proposal_contract"]["apply_command_available"] is True, cli_proposal_payload + assert cli_proposal_payload["proposal_contract"]["target_file_sha256_required_for_apply"] is True, cli_proposal_payload + assert cli_proposal_payload["proposal_contract"]["approval_draft_supported"] is True, cli_proposal_payload + + policy_path = cli_skill_dir / "references" / "user-memory-policy.md" + policy_path.parent.mkdir(parents=True, exist_ok=True) + policy_path.write_text("old policy\n", encoding="utf-8") + verifier = cli_skill_dir / "tests" / "check_policy.py" + verifier.parent.mkdir(parents=True, exist_ok=True) + verifier.write_text( + "from pathlib import Path\n" + "assert 'approved adaptive note' in Path('references/user-memory-policy.md').read_text(encoding='utf-8')\n", + encoding="utf-8", + ) + approval_proposal = next( + item for item in cli_proposal_payload["proposals"] if item["pattern_id"] == "approval_safety" + ) + patch_text = ( + "diff --git a/references/user-memory-policy.md b/references/user-memory-policy.md\n" + "--- a/references/user-memory-policy.md\n" + "+++ b/references/user-memory-policy.md\n" + "@@ -1 +1,2 @@\n" + " old policy\n" + "+approved adaptive note\n" + ) + patch_path = TMP / "approved-adaptation.patch" + patch_path.write_text(patch_text, encoding="utf-8") + ledger_path = cli_skill_dir / "reports" / "adaptation_approval_ledger.json" + prepare_proc = run_script( + str(CLI), + "adapt-apply", + str(cli_skill_dir), + "--proposal-id", + approval_proposal["proposal_id"], + "--patch-file", + str(patch_path), + "--generated-at", + "2026-06-15T00:00:00Z", + "--today", + "2026-06-15", + "--prepare-approval", + ) + assert prepare_proc.returncode == 0, prepare_proc.stdout + prepare_payload = json.loads(prepare_proc.stdout) + assert_report_contract_mirrors(prepare_payload, "apply_contract") + assert prepare_payload["summary"]["approval_draft_count"] == 1, prepare_payload + assert prepare_payload["approval_draft_count"] == 1, prepare_payload + assert prepare_payload["approval_draft"]["decision"] == "pending-review", prepare_payload + assert prepare_payload["approval_draft"]["patch_sha256"] == sha256_text(patch_text), prepare_payload + assert prepare_payload["approval_draft"]["target_file_sha256"] == { + "references/user-memory-policy.md": sha256_text("old policy\n"), + }, prepare_payload + approval_ledger = json.loads(ledger_path.read_text(encoding="utf-8")) + assert_report_contract_mirrors(approval_ledger, "approval_contract") + assert approval_ledger["summary"]["pending_review_count"] == 1, approval_ledger + assert approval_ledger["pending_review_count"] == 1, approval_ledger + approval_ledger["entries"][0].update( + { + "decision": "approved", + "reviewer": "qa-reviewer", + "reason": "Fixture validates approval-gated adaptive apply.", + "approved_at": "2026-06-15", + "expires_at": "2026-12-31", + "verification_commands": ["python3 tests/check_policy.py"], + "rollback_plan": "git apply -R approved-adaptation.patch", + } + ) + ledger_path.write_text(json.dumps(approval_ledger, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + policy_path.write_text("changed outside approval\n", encoding="utf-8") + stale_proc = run_script( + str(CLI), + "adapt-apply", + str(cli_skill_dir), + "--proposal-id", + approval_proposal["proposal_id"], + "--patch-file", + str(patch_path), + "--output-json", + str(cli_skill_dir / "reports" / "stale_baseline_regression.json"), + "--output-md", + str(cli_skill_dir / "reports" / "stale_baseline_regression.md"), + "--generated-at", + "2026-06-15T00:00:00Z", + "--today", + "2026-06-15", + ) + assert stale_proc.returncode == 2, stale_proc.stdout + stale_payload = json.loads(stale_proc.stdout) + assert any("baseline sha256" in item for item in stale_payload["failures"]), stale_payload + assert "approved adaptive note" not in policy_path.read_text(encoding="utf-8"), policy_path + policy_path.write_text("old policy\n", encoding="utf-8") + dry_run = run_script( + str(CLI), + "adapt-apply", + str(cli_skill_dir), + "--proposal-id", + approval_proposal["proposal_id"], + "--patch-file", + str(patch_path), + "--generated-at", + "2026-06-15T00:00:00Z", + "--today", + "2026-06-15", + ) + assert dry_run.returncode == 0, dry_run.stderr + dry_payload = json.loads(dry_run.stdout) + assert_report_contract_mirrors(dry_payload, "apply_contract") + assert dry_payload["summary"]["dry_run_count"] == 1, dry_payload + assert dry_payload["dry_run_count"] == 1, dry_payload + assert dry_payload["summary"]["applied_count"] == 0, dry_payload + refreshed_ledger = json.loads(ledger_path.read_text(encoding="utf-8")) + assert_report_contract_mirrors(refreshed_ledger, "approval_contract") + assert refreshed_ledger["summary"]["approval_count"] == 1, refreshed_ledger + assert refreshed_ledger["approval_count"] == 1, refreshed_ledger + assert refreshed_ledger["summary"]["pending_review_count"] == 0, refreshed_ledger + assert "approved adaptive note" not in policy_path.read_text(encoding="utf-8"), policy_path + + apply_proc = run_script( + str(CLI), + "adapt-apply", + str(cli_skill_dir), + "--proposal-id", + approval_proposal["proposal_id"], + "--patch-file", + str(patch_path), + "--generated-at", + "2026-06-15T00:00:00Z", + "--today", + "2026-06-15", + "--apply", + "--run-verification", + ) + assert apply_proc.returncode == 0, apply_proc.stderr + apply_payload = json.loads(apply_proc.stdout) + assert_report_contract_mirrors(apply_payload, "apply_contract") + assert apply_payload["summary"]["applied_count"] == 1, apply_payload + assert apply_payload["applied_count"] == 1, apply_payload + assert apply_payload["summary"]["regression_run_count"] == 1, apply_payload + assert apply_payload["summary"]["regression_pass_count"] == 1, apply_payload + assert "approved adaptive note" in policy_path.read_text(encoding="utf-8"), policy_path + + policy_path.write_text("old policy\n", encoding="utf-8") + failing_verifier = cli_skill_dir / "tests" / "failing_policy.py" + failing_verifier.write_text( + "from pathlib import Path\n" + "assert 'impossible verifier token' in Path('references/user-memory-policy.md').read_text(encoding='utf-8')\n", + encoding="utf-8", + ) + rollback_patch = ( + "diff --git a/references/user-memory-policy.md b/references/user-memory-policy.md\n" + "--- a/references/user-memory-policy.md\n" + "+++ b/references/user-memory-policy.md\n" + "@@ -1 +1,2 @@\n" + " old policy\n" + "+rolled back adaptive note\n" + ) + rollback_patch_path = TMP / "rollback-adaptation.patch" + rollback_patch_path.write_text(rollback_patch, encoding="utf-8") + approval_ledger["entries"][0]["patch_sha256"] = sha256_text(rollback_patch) + approval_ledger["entries"][0]["verification_commands"] = ["python3 tests/failing_policy.py"] + ledger_path.write_text(json.dumps(approval_ledger, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + rollback_proc = run_script( + str(CLI), + "adapt-apply", + str(cli_skill_dir), + "--proposal-id", + approval_proposal["proposal_id"], + "--patch-file", + str(rollback_patch_path), + "--output-json", + str(cli_skill_dir / "reports" / "rollback_adaptation_regression.json"), + "--output-md", + str(cli_skill_dir / "reports" / "rollback_adaptation_regression.md"), + "--today", + "2026-06-15", + "--apply", + "--run-verification", + ) + assert rollback_proc.returncode == 2, rollback_proc.stdout + rollback_payload = json.loads(rollback_proc.stdout) + assert_report_contract_mirrors(rollback_payload, "apply_contract") + assert rollback_payload["summary"]["applied_count"] == 0, rollback_payload + assert rollback_payload["rollback_count"] == 1, rollback_payload + assert rollback_payload["summary"]["rollback_count"] == 1, rollback_payload + assert rollback_payload["attempts"][0]["status"] == "failed-rolled-back", rollback_payload + assert rollback_payload["attempts"][0]["rollback_result"]["ok"] is True, rollback_payload + assert "rolled back adaptive note" not in policy_path.read_text(encoding="utf-8"), policy_path + assert policy_path.read_text(encoding="utf-8") == "old policy\n", policy_path + + unsafe_patch = ( + "diff --git a/README.md b/README.md\n" + "--- a/README.md\n" + "+++ b/README.md\n" + "@@ -1 +1,2 @@\n" + " old\n" + "+unsafe\n" + ) + unsafe_patch_path = TMP / "unsafe-adaptation.patch" + unsafe_patch_path.write_text(unsafe_patch, encoding="utf-8") + approval_ledger["entries"][0]["patch_sha256"] = sha256_text(unsafe_patch) + ledger_path.write_text(json.dumps(approval_ledger, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + unsafe_proc = run_script( + str(CLI), + "adapt-apply", + str(cli_skill_dir), + "--proposal-id", + approval_proposal["proposal_id"], + "--patch-file", + str(unsafe_patch_path), + "--output-json", + str(cli_skill_dir / "reports" / "unsafe_adaptation_regression.json"), + "--output-md", + str(cli_skill_dir / "reports" / "unsafe_adaptation_regression.md"), + "--today", + "2026-06-15", + ) + assert unsafe_proc.returncode == 2, unsafe_proc.stdout + unsafe_payload = json.loads(unsafe_proc.stdout) + assert_report_contract_mirrors(unsafe_payload, "apply_contract") + assert any("outside approval target_files" in item for item in unsafe_payload["failures"]), unsafe_payload + + proposal_report_path = cli_skill_dir / "reports" / "adaptation_proposals.json" + proposal_report = json.loads(proposal_report_path.read_text(encoding="utf-8")) + for proposal in proposal_report["proposals"]: + if proposal["proposal_id"] == approval_proposal["proposal_id"]: + proposal["target_files"].append("references/new-user-memory-policy.md") + proposal_report_path.write_text(json.dumps(proposal_report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + new_file_patch = ( + "diff --git a/references/new-user-memory-policy.md b/references/new-user-memory-policy.md\n" + "new file mode 100644\n" + "--- /dev/null\n" + "+++ b/references/new-user-memory-policy.md\n" + "@@ -0,0 +1 @@\n" + "+new approved policy\n" + ) + new_file_patch_path = TMP / "new-file-adaptation.patch" + new_file_patch_path.write_text(new_file_patch, encoding="utf-8") + new_file_prepare = run_script( + str(CLI), + "adapt-apply", + str(cli_skill_dir), + "--proposal-id", + approval_proposal["proposal_id"], + "--patch-file", + str(new_file_patch_path), + "--approval-ledger", + str(cli_skill_dir / "reports" / "new_file_approval_ledger.json"), + "--output-json", + str(cli_skill_dir / "reports" / "new_file_prepare_regression.json"), + "--output-md", + str(cli_skill_dir / "reports" / "new_file_prepare_regression.md"), + "--generated-at", + "2026-06-15T00:00:00Z", + "--today", + "2026-06-15", + "--prepare-approval", + ) + assert new_file_prepare.returncode == 0, new_file_prepare.stdout + new_file_payload = json.loads(new_file_prepare.stdout) + assert_report_contract_mirrors(new_file_payload, "apply_contract") + assert new_file_payload["approval_draft"]["target_file_sha256"] == { + "references/new-user-memory-policy.md": "__absent__", + }, new_file_payload + assert not (cli_skill_dir / "references" / "new-user-memory-policy.md").exists(), cli_skill_dir + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_adapter_snapshots.py b/tests/verify_adapter_snapshots.py new file mode 100644 index 0000000..22f38d4 --- /dev/null +++ b/tests/verify_adapter_snapshots.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path +import yaml + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "cross_packager.py" +TRUST_SCRIPT = ROOT / "scripts" / "trust_check.py" +EXPECTATIONS = ROOT / "evals" / "packaging_expectations.json" +SNAPSHOTS = ROOT / "tests" / "snapshots" +TMP = ROOT / "tests" / "tmp_snapshot" + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + trust_proc = subprocess.run( + [sys.executable, str(TRUST_SCRIPT), str(ROOT)], + cwd=ROOT, + capture_output=True, + text=True, + ) + if trust_proc.returncode != 0: + print(trust_proc.stdout) + print(trust_proc.stderr) + raise SystemExit(trust_proc.returncode) + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--platform", + "openai", + "--platform", + "claude", + "--platform", + "generic", + "--platform", + "vscode", + "--expectations", + str(EXPECTATIONS), + "--output-dir", + str(TMP), + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + if proc.returncode != 0: + print(proc.stdout) + print(proc.stderr) + raise SystemExit(proc.returncode) + + failures = [] + for name in ("openai", "claude", "generic", "vscode"): + snapshot = json.loads((SNAPSHOTS / f"{name}_adapter.json").read_text(encoding="utf-8")) + adapter = json.loads((TMP / "targets" / name / "adapter.json").read_text(encoding="utf-8")) + if adapter.get("platform") != snapshot["platform"]: + failures.append(f"{name}: platform mismatch") + if adapter.get("canonical_metadata") != snapshot["canonical_metadata"]: + failures.append(f"{name}: canonical metadata mismatch") + for field in snapshot.get("required_fields", []): + if field not in adapter: + failures.append(f"{name}: missing required adapter field {field}") + if adapter.get("ir_source") != "skill-ir/examples/yao-meta-skill.json": + failures.append(f"{name}: adapter is not sourced from root Skill IR") + if adapter.get("ir_schema_version") != "2.0.0": + failures.append(f"{name}: missing Skill IR schema version") + contract = adapter.get("semantic_contract", {}) + if contract.get("name") != "yao-meta-skill": + failures.append(f"{name}: semantic contract name mismatch") + if contract.get("trigger_description") != adapter.get("description"): + failures.append(f"{name}: trigger description is not adapter description") + if contract.get("job_to_be_done") != adapter.get("job_to_be_done"): + failures.append(f"{name}: job-to-be-done not carried into adapter") + if contract.get("resource_counts", {}).get("references", 0) <= 0: + failures.append(f"{name}: semantic contract does not include reference counts") + if contract.get("eval_counts", {}).get("output", 0) <= 0: + failures.append(f"{name}: semantic contract does not include output eval counts") + parity = adapter.get("semantic_parity", {}) + if parity.get("source") != "skill-ir": + failures.append(f"{name}: semantic parity source is not skill-ir") + if parity.get("name_matches_ir") is not True: + failures.append(f"{name}: frontmatter name does not match Skill IR") + if parity.get("description_matches_ir") is not True: + failures.append(f"{name}: frontmatter description does not match Skill IR") + compiler = adapter.get("compiler", {}) + if compiler.get("name") != "yao-skill-ir-compiler": + failures.append(f"{name}: missing compiler provenance") + compiled = adapter.get("compiled_contract", {}) + if compiled.get("target") != name: + failures.append(f"{name}: compiled contract target mismatch") + if compiled.get("trigger", {}).get("description") != adapter.get("description"): + failures.append(f"{name}: compiled trigger description is not adapter description") + permission = adapter.get("permission_contract", {}) + if permission.get("source") != "reports/security_trust_report.json": + failures.append(f"{name}: permission contract is not sourced from trust report") + if "file_write" not in permission.get("declared_capabilities", []): + failures.append(f"{name}: permission contract does not carry file_write capability") + if permission.get("help_smoke", {}).get("failed_count") != 0: + failures.append(f"{name}: permission contract reports help smoke failures") + target_permission = adapter.get("target_permission_contract", {}) + if target_permission.get("target") != name: + failures.append(f"{name}: target permission contract target mismatch") + if not target_permission.get("representation"): + failures.append(f"{name}: target permission contract lacks representation") + if target_permission.get("capability_counts", {}).get("file_write", 0) <= 0: + failures.append(f"{name}: target permission contract lacks file_write count") + native = adapter.get("target_native_contract", {}) + if native.get("target") != name: + failures.append(f"{name}: target native contract target mismatch") + if not native.get("native_surface"): + failures.append(f"{name}: target native contract lacks native surface") + if not native.get("activation", {}).get("policy"): + failures.append(f"{name}: target native contract lacks activation policy") + if not native.get("resources", {}).get("strategy"): + failures.append(f"{name}: target native contract lacks resource strategy") + if not native.get("scripts", {}).get("strategy"): + failures.append(f"{name}: target native contract lacks script strategy") + if not native.get("permissions", {}).get("enforcement"): + failures.append(f"{name}: target native contract lacks permission enforcement") + if not native.get("review", {}).get("artifacts"): + failures.append(f"{name}: target native contract lacks review artifacts") + transform = adapter.get("target_transform", {}) + if transform.get("target") != name: + failures.append(f"{name}: target transform target mismatch") + if not transform.get("generated_files"): + failures.append(f"{name}: target transform does not declare generated files") + if not transform.get("permission_representation"): + failures.append(f"{name}: target transform does not declare permission representation") + if transform.get("native_surface") != native.get("native_surface"): + failures.append(f"{name}: target transform native surface does not match native contract") + if transform.get("activation_policy") != native.get("activation", {}).get("policy"): + failures.append(f"{name}: target transform activation policy does not match native contract") + if not (TMP / snapshot["required_generated_file"]).exists(): + failures.append(f"{name}: missing generated file {snapshot['required_generated_file']}") + if name == "openai": + meta = yaml.safe_load((TMP / "targets" / "openai" / "agents" / "openai.yaml").read_text(encoding="utf-8")) or {} + compatibility = meta.get("compatibility", {}) + for field in ("canonical_format", "activation_mode", "execution_context", "shell", "trust_level", "remote_inline_execution", "degradation_strategy"): + if not compatibility.get(field): + failures.append(f"{name}: missing portability metadata in generated openai.yaml: {field}") + yaml_permission = compatibility.get("permission_contract", {}) + if "file_write" not in yaml_permission.get("declared_capabilities", []): + failures.append(f"{name}: generated openai.yaml does not carry permission capabilities") + native_yaml = compatibility.get("native_contract", {}) + if native_yaml.get("native_surface") != native.get("native_surface"): + failures.append(f"{name}: generated openai.yaml does not carry native surface") + if name == "claude": + readme = (TMP / "targets" / "claude" / "README.md").read_text(encoding="utf-8") + if "Native surface:" not in readme or "Activation:" not in readme: + failures.append(f"{name}: generated README does not describe native behavior") + if name == "vscode": + readme = (TMP / "targets" / "vscode" / "README.md").read_text(encoding="utf-8") + if "VS Code / Copilot Agent Skills Package" not in readme or "workspace trust" not in readme: + failures.append(f"{name}: generated README does not describe VS Code install and trust behavior") + + report = {"ok": not failures, "failures": failures} + print(json.dumps(report, ensure_ascii=False, indent=2)) + if failures: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_adoption_drift.py b/tests/verify_adoption_drift.py new file mode 100644 index 0000000..03bdf2f --- /dev/null +++ b/tests/verify_adoption_drift.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +import zipfile +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_adoption_drift" +SCRIPT = ROOT / "scripts" / "render_adoption_drift_report.py" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stderr": proc.stderr, + } + + +def record(skill_dir: Path, *args: str) -> dict: + return run([sys.executable, str(SCRIPT), str(skill_dir), *args]) + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "telemetry-demo", + "--description", + "Turn repeated operational signals into a reusable telemetry demo skill.", + "--output-dir", + str(TMP), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = TMP / "telemetry-demo" + + assert record( + skill_dir, + "--record-event", + "skill_activation", + "--activation-type", + "explicit", + "--outcome", + "accepted", + "--timestamp", + "2026-06-13T10:00:00Z", + )["ok"] + assert record( + skill_dir, + "--record-event", + "skill_activation", + "--activation-type", + "implicit", + "--outcome", + "edited", + "--timestamp", + "2026-06-13T10:01:00Z", + )["ok"] + assert record( + skill_dir, + "--record-event", + "skill_activation", + "--activation-type", + "implicit", + "--outcome", + "missed", + "--failure-type", + "under_trigger", + "--timestamp", + "2026-06-13T10:02:00Z", + )["ok"] + assert record( + skill_dir, + "--record-event", + "skill_output", + "--outcome", + "rejected", + "--failure-type", + "bad_output", + "--timestamp", + "2026-06-13T10:03:00Z", + )["ok"] + final = record( + skill_dir, + "--record-event", + "script_run", + "--outcome", + "failed", + "--failure-type", + "script_error", + "--source", + "yao_cli", + "--command", + "validate", + "--timestamp", + "2026-06-13T10:04:00Z", + ) + assert final["ok"], final + summary = final["payload"]["summary"] + assert summary["event_count"] == 5, summary + assert summary["adoption_sample_count"] == 4, summary + assert summary["adoption_rate"] == 50.0, summary + assert summary["missed_trigger_count"] == 2, summary + assert summary["bad_output_count"] == 1, summary + assert summary["script_error_count"] == 1, summary + assert summary["risk_band"] == "high", summary + assert summary["source_types"]["yao_cli"] == 1, summary + assert summary["command_counts"]["validate"] == 1, summary + assert final["payload"]["privacy_contract"]["raw_content_allowed"] is False, final + assert final["payload"]["next_iteration_candidates"], final + markdown = (skill_dir / "reports" / "adoption_drift_report.md").read_text(encoding="utf-8") + assert "metadata-only telemetry" in markdown, markdown + assert "Raw user prompts" in markdown, markdown + assert "source=`yao_cli` command=`validate`" in markdown, markdown + + unsafe_events = TMP / "unsafe_events.jsonl" + unsafe_events.write_text( + json.dumps( + { + "event": "skill_activation", + "outcome": "accepted", + "prompt": "raw prompt must not be stored", + } + ) + + "\n", + encoding="utf-8", + ) + unsafe = run( + [ + sys.executable, + str(SCRIPT), + str(skill_dir), + "--events-jsonl", + str(unsafe_events), + "--output-json", + str(TMP / "unsafe.json"), + "--output-md", + str(TMP / "unsafe.md"), + ] + ) + assert unsafe["returncode"] == 2, unsafe + assert not unsafe["payload"]["ok"], unsafe + assert "raw content fields" in unsafe["payload"]["failures"][0], unsafe + + root_events = ROOT / "reports" / "telemetry_events.jsonl" + original_root_events = root_events.read_text(encoding="utf-8") if root_events.exists() else None + root_events.write_text( + json.dumps( + { + "event": "skill_activation", + "skill": "yao-meta-skill", + "version": "1.1.0", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "timestamp": "2026-06-13T10:05:00Z", + } + ) + + "\n", + encoding="utf-8", + ) + try: + package_dir = TMP / "dist" + package = run( + [ + sys.executable, + str(ROOT / "scripts" / "cross_packager.py"), + str(ROOT), + "--platform", + "generic", + "--output-dir", + str(package_dir), + "--zip", + ] + ) + assert package["ok"], package + with zipfile.ZipFile(package_dir / "yao-meta-skill.zip") as archive: + names = archive.namelist() + assert "yao-meta-skill/reports/telemetry_events.jsonl" not in names, names + finally: + if original_root_events is not None: + root_events.write_text(original_root_events, encoding="utf-8") + elif root_events.exists(): + root_events.unlink() + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_architecture_maintainability.py b/tests/verify_architecture_maintainability.py new file mode 100644 index 0000000..a8f96f2 --- /dev/null +++ b/tests/verify_architecture_maintainability.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_architecture_maintainability.py" + + +def main() -> None: + tmp = ROOT / "tests" / "tmp_architecture_maintainability" + if tmp.exists(): + shutil.rmtree(tmp) + tmp.mkdir(parents=True, exist_ok=True) + + output_json = tmp / "architecture_maintainability.json" + output_md = tmp / "architecture_maintainability.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-14", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["ok"], payload + assert payload["summary"]["decision"] == "pass", payload["summary"] + assert payload["summary"]["early_watch_line_threshold"] == 600, payload["summary"] + assert payload["summary"]["watch_line_threshold"] == 720, payload["summary"] + assert payload["summary"]["early_watchlist_count"] >= 2, payload["summary"] + assert payload["summary"]["watchlist_count"] == 0, payload["summary"] + assert payload["summary"]["hotspot_count"] == 0, payload["summary"] + assert payload["summary"]["blocker_count"] == 0, payload["summary"] + assert payload["summary"]["command_handler_count"] >= 60, payload["summary"] + assert payload["summary"]["entrypoint_command_handler_count"] < 30, payload["summary"] + assert payload["summary"]["command_module_count"] >= 5, payload["summary"] + assert payload["summary"]["largest_file_lines"] < 900, payload["summary"] + assert all(item["severity"] == "pass" for item in payload["largest_files"]), payload["largest_files"] + assert payload["watchlist"] == [], payload["watchlist"] + assert payload["early_watchlist"], payload["early_watchlist"] + assert all(item["early_watch"] is True for item in payload["early_watchlist"]), payload["early_watchlist"] + early_watch_paths = {item["path"] for item in payload["early_watchlist"]} + assert "scripts/render_review_viewer.py" not in early_watch_paths, payload["early_watchlist"] + assert "scripts/render_skill_os2_coverage.py" not in early_watch_paths, payload["early_watchlist"] + assert "scripts/render_reference_synthesis.py" not in early_watch_paths, payload["early_watchlist"] + assert "scripts/build_skill_atlas.py" not in early_watch_paths, payload["early_watchlist"] + assert "scripts/skill_report_model.py" not in early_watch_paths, payload["early_watchlist"] + assert "scripts/render_review_studio.py" not in early_watch_paths, payload["early_watchlist"] + assert "scripts/review_studio_gates.py" not in early_watch_paths, payload["early_watchlist"] + assert "scripts/cross_packager.py" not in early_watch_paths, payload["early_watchlist"] + renderer_lines = len((ROOT / "scripts" / "render_review_studio.py").read_text(encoding="utf-8").splitlines()) + action_module = (ROOT / "scripts" / "review_studio_actions.py").read_text(encoding="utf-8") + action_lines = len(action_module.splitlines()) + assert renderer_lines < 600, renderer_lines + assert action_lines < 450, action_lines + assert 'SCRIPT_INTERFACE = "internal-module"' in action_module, action_module[:400] + hotspot_paths = {item["path"] for item in payload["hotspots"]} + assert "scripts/yao.py" not in hotspot_paths, hotspot_paths + assert "scripts/render_review_studio.py" not in hotspot_paths, hotspot_paths + assert "scripts/review_studio_actions.py" not in hotspot_paths, hotspot_paths + assert "scripts/render_review_viewer.py" not in hotspot_paths, hotspot_paths + assert output_json.exists(), output_json + markdown = output_md.read_text(encoding="utf-8") + assert "# Architecture Maintainability" in markdown, markdown + assert "No file-size hotspots found." in markdown, markdown + assert "No near-threshold files found." in markdown, markdown + assert "## Early Watchlist" in markdown, markdown + assert f"- early watchlist: `{payload['summary']['early_watchlist_count']}`" in markdown, markdown + early_watch_markdown = markdown.split("## Early Watchlist", 1)[1].split("## Largest Files", 1)[0] + assert "scripts/render_review_viewer.py" not in markdown, markdown + assert "scripts/render_skill_os2_coverage.py" not in markdown, markdown + assert "scripts/render_reference_synthesis.py" not in markdown, markdown + assert "scripts/build_skill_atlas.py" not in early_watch_markdown, early_watch_markdown + assert "scripts/skill_report_model.py" not in early_watch_markdown, early_watch_markdown + assert "scripts/render_review_studio.py" not in early_watch_markdown, early_watch_markdown + assert "scripts/review_studio_gates.py" not in early_watch_markdown, early_watch_markdown + assert "scripts/cross_packager.py" not in early_watch_markdown, early_watch_markdown + assert "Do not split a file only for line count" in markdown, markdown + + blocker_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(tmp / "architecture_blocker.json"), + "--output-md", + str(tmp / "architecture_blocker.md"), + "--warn-lines", + "10", + "--block-lines", + "100", + "--generated-at", + "2026-06-14", + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + assert blocker_proc.returncode == 2, blocker_proc.stdout + blocker_payload = json.loads(blocker_proc.stdout) + assert blocker_payload["ok"] is False, blocker_payload + assert blocker_payload["summary"]["blocker_count"] > 0, blocker_payload["summary"] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_artifact_design_profile.py b/tests/verify_artifact_design_profile.py new file mode 100644 index 0000000..3af09d9 --- /dev/null +++ b/tests/verify_artifact_design_profile.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_artifact_design_profile" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "visual-report-demo", + "--description", + "Create polished HTML reports and Markdown tutorials with screenshots, comparison tables, and reviewer notes.", + "--output-dir", + str(tmp_root), + "--intent-primary-output", + "A polished HTML report and Markdown tutorial.", + "--intent-real-input", + "screenshots", + "--intent-real-input", + "comparison data", + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = tmp_root / "visual-report-demo" + proc = subprocess.run( + [sys.executable, str(ROOT / "scripts" / "render_artifact_design_profile.py"), str(skill_dir)], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + summary = payload["summary"] + keys = {item["key"] for item in summary["artifact_families"]} + assert "report" in keys, summary + assert "tutorial" in keys, summary + assert summary["quality_gates"], summary + assert summary["layout_patterns"], summary + assert any("parchment" in item for item in summary["anti_patterns"]), summary + markdown = (skill_dir / "reports" / "artifact-design-profile.md").read_text(encoding="utf-8") + assert "Artifact Design Profile" in markdown, markdown[:500] + assert "Quality Gates" in markdown, markdown[:1200] + assert "Do not copy Kami" in markdown, markdown[:2000] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_baseline_compare.py b/tests/verify_baseline_compare.py new file mode 100644 index 0000000..65e6f2a --- /dev/null +++ b/tests/verify_baseline_compare.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_baseline_compare.py" + + +def main() -> None: + output_json = ROOT / "tests" / "tmp_baseline_compare.json" + output_md = ROOT / "tests" / "tmp_baseline_compare.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--entry", + f"root::{ROOT / 'reports' / 'description_optimization.json'}", + "--entry", + f"team-frontend-review::{ROOT / 'examples' / 'team-frontend-review' / 'optimization' / 'reports' / 'description_optimization.json'}", + "--entry", + f"governed-incident-command::{ROOT / 'examples' / 'governed-incident-command' / 'optimization' / 'reports' / 'description_optimization.json'}", + "--output-json", + str(output_json), + "--output-md", + str(output_md), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + assert payload["summary"]["target_count"] == 3, payload + assert output_json.exists(), output_json + assert output_md.exists(), output_md + assert "Target Breakdown" in output_md.read_text(encoding="utf-8") + output_json.unlink(missing_ok=True) + output_md.unlink(missing_ok=True) + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_benchmark_reproducibility.py b/tests/verify_benchmark_reproducibility.py new file mode 100644 index 0000000..ce97343 --- /dev/null +++ b/tests/verify_benchmark_reproducibility.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_benchmark_reproducibility.py" +TMP = ROOT / "tests" / "tmp_benchmark_reproducibility" +sys.path.insert(0, str(ROOT / "scripts")) +from evidence_consistency_core import beta_public_claim_split_values # noqa: E402 +from render_benchmark_reproducibility import beta_deferred_evidence # noqa: E402 + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "render_world_class_evidence_ledger.py"), + str(ROOT), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "render_world_class_evidence_intake.py"), + str(ROOT), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "render_world_class_preflight.py"), + str(ROOT), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + submission_review_proc = subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "render_world_class_submission_review.py"), + str(ROOT), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=False, + ) + assert submission_review_proc.returncode in {0, 2}, submission_review_proc.stderr + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "render_world_class_operator_runbook.py"), + str(ROOT), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "render_world_class_claim_guard.py"), + str(ROOT), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "python_compat_check.py"), + str(ROOT), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + output_json = TMP / "benchmark_reproducibility.json" + output_md = TMP / "benchmark_reproducibility.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + assert payload["summary"]["reproducibility_ready"] is True, payload + assert payload["summary"]["release_lock_ready"] == (payload["git_status"]["source_dirty"] is False), payload + assert payload["summary"]["methodology_complete"] is True, payload + assert payload["summary"]["missing_artifact_count"] == 0, payload + assert len(payload["summary"]["evidence_bundle_sha256"]) == 64, payload + assert payload["summary"]["evidence_bundle_sha256"] == payload["evidence_bundle"]["sha256"], payload + assert payload["summary"]["source_contract_sha256"], payload + assert payload["summary"]["archive_sha256"], payload + assert payload["summary"]["output_case_count"] >= 5, payload + assert payload["summary"]["failure_disclosure_count"] >= 1, payload + assert payload["summary"]["command_count"] >= 10, payload + assert "working_tree_dirty" in payload["summary"], payload + assert "source_tree_dirty" in payload["summary"], payload + assert "generated_tree_dirty" in payload["summary"], payload + assert payload["summary"]["source_changed_file_count"] == payload["git_status"]["source_changed_file_count"], payload + assert payload["summary"]["generated_changed_file_count"] == payload["git_status"][ + "generated_changed_file_count" + ], payload + assert payload["git_status"]["available"] is True, payload + assert payload["git_status"]["scope"] == "generation-time status before this report is written", payload + assert isinstance(payload["git_status"]["generated_dirty_prefixes"], list), payload + assert payload["release_lock"]["status_scope"] == "generation-time status before this report is written", payload + assert payload["release_lock"]["commit"] == payload["commit"], payload + assert payload["release_lock"]["source_changed_file_count"] == payload["git_status"][ + "source_changed_file_count" + ], payload + assert payload["release_lock"]["generated_changed_file_count"] == payload["git_status"][ + "generated_changed_file_count" + ], payload + assert payload["evidence_bundle"]["algorithm"] == "sha256(path,label,exists,artifact_sha256)", payload + assert payload["evidence_bundle"]["artifact_count"] == payload["summary"]["required_artifact_count"], payload + assert payload["evidence_bundle"]["existing_count"] == payload["summary"]["required_artifact_count"], payload + assert payload["evidence_bundle"]["missing_count"] == 0, payload + assert payload["evidence_bundle"]["missing_paths"] == [], payload + assert payload["summary"]["provider_evidence_complete"] is True, payload + assert payload["summary"]["human_review_complete"] is False, payload + assert payload["summary"]["world_class_ready"] is False, payload + assert payload["summary"]["world_class_source_check_count"] >= 13, payload + assert payload["summary"]["world_class_source_pass_count"] >= 6, payload + assert payload["summary"]["world_class_source_blocked_count"] >= 6, payload + assert ( + payload["summary"]["world_class_source_pass_count"] + + payload["summary"]["world_class_source_blocked_count"] + == payload["summary"]["world_class_source_check_count"] + ), payload + expected_beta_ready = ( + payload["summary"]["reproducibility_ready"] + and payload["summary"]["release_lock_ready"] + and payload["summary"]["provider_evidence_complete"] + ) + assert payload["summary"]["beta_test_ready"] is expected_beta_ready, payload + assert payload["summary"]["beta_test_blocker_count"] == len(payload["beta_test_release"]["blockers"]), payload + assert payload["summary"]["beta_test_deferred_evidence_count"] == len( + payload["beta_test_release"]["allowed_deferred_evidence"] + ), payload + assert payload["beta_test_release"]["ready"] is expected_beta_ready, payload["beta_test_release"] + assert "beta/public test release" in payload["beta_test_release"]["scope"], payload["beta_test_release"] + assert "Human blind-review" in payload["beta_test_release"]["policy"], payload["beta_test_release"] + assert "do not claim world-class" in payload["beta_test_release"]["required_wording"], payload[ + "beta_test_release" + ] + ledger_payload = json.loads((ROOT / "reports" / "world_class_evidence_ledger.json").read_text(encoding="utf-8")) + pending_ledger_keys = { + item["key"] for item in ledger_payload["entries"] if item.get("status") == "pending" + } + deferred_keys = {item["key"] for item in payload["beta_test_release"]["allowed_deferred_evidence"]} + assert deferred_keys == pending_ledger_keys, payload["beta_test_release"] + assert "human-adjudication" in deferred_keys, payload["beta_test_release"] + assert "provider-holdout" in deferred_keys, payload["beta_test_release"] + assert "provider-ledger-review" not in deferred_keys, payload["beta_test_release"] + assert not any("human blind-review" in item for item in payload["beta_test_release"]["blockers"]), payload[ + "beta_test_release" + ] + synthetic_deferred = beta_deferred_evidence( + { + "entries": [ + {"key": "provider-holdout", "label": "Provider Holdout", "status": "accepted"}, + {"key": "native-client-telemetry", "label": "Native Client Telemetry", "status": "pending"}, + ] + } + ) + assert {item["key"] for item in synthetic_deferred} == {"native-client-telemetry"}, synthetic_deferred + expected_beta_split, actual_beta_split = beta_public_claim_split_values( + { + "summary": { + "reproducibility_ready": True, + "release_lock_ready": True, + "provider_evidence_complete": True, + "beta_test_ready": True, + "public_claim_ready": False, + "human_review_complete": True, + }, + "beta_test_release": { + "ready": True, + "scope": "beta/public test release without superiority, fully-reviewed, or world-class claims", + "allowed_deferred_evidence": [{"key": "native-client-telemetry", "reason": "still pending"}], + }, + }, + { + "summary": {"ready_to_claim_world_class": False}, + "entries": [ + {"key": "human-adjudication", "status": "accepted"}, + {"key": "native-client-telemetry", "status": "pending"}, + ], + }, + {"data": {"output_review_adjudication": {"summary": {"pair_count": 5, "pending_count": 0}}}}, + ) + assert expected_beta_split == actual_beta_split, (expected_beta_split, actual_beta_split) + assert expected_beta_split["human_review_complete"] is True, expected_beta_split + assert expected_beta_split["deferred_human_review"] is False, expected_beta_split + assert payload["summary"]["public_claim_ready"] is False, payload + minimum_blockers = 3 if payload["summary"]["release_lock_ready"] else 4 + assert payload["summary"]["public_claim_blocker_count"] >= minimum_blockers, payload + assert payload["public_claim"]["ready"] is False, payload["public_claim"] + if payload["summary"]["release_lock_ready"]: + assert not any("release lock" in item for item in payload["public_claim"]["blockers"]), payload["public_claim"] + else: + assert any("release lock" in item for item in payload["public_claim"]["blockers"]), payload["public_claim"] + assert not any("provider-backed model holdout evidence is incomplete" in item for item in payload["public_claim"]["blockers"]), payload["public_claim"] + assert any("human blind-review" in item for item in payload["public_claim"]["blockers"]), payload["public_claim"] + assert any("world-class evidence" in item for item in payload["public_claim"]["blockers"]), payload["public_claim"] + assert any("world-class source checks" in item for item in payload["public_claim"]["blockers"]), payload["public_claim"] + assert "complete source checks" in payload["public_claim"]["policy"], payload["public_claim"] + headings = {item["heading"]: item["exists"] for item in payload["methodology"]["sections"]} + assert headings["## Benchmark Types"], headings + assert headings["## Failure Disclosure"], headings + artifacts = {item["path"]: item for item in payload["artifacts_checked"]} + assert artifacts["reports/benchmark_methodology.md"]["exists"], artifacts + assert artifacts["evals/failure-cases.md"]["exists"], artifacts + assert artifacts["reports/world_class_evidence_plan.json"]["exists"], artifacts + assert artifacts["reports/world_class_evidence_ledger.json"]["exists"], artifacts + assert artifacts["reports/world_class_evidence_intake.json"]["exists"], artifacts + assert artifacts["reports/world_class_evidence_preflight.json"]["exists"], artifacts + assert artifacts["reports/world_class_submission_review.json"]["exists"], artifacts + assert artifacts["reports/world_class_operator_runbook.json"]["exists"], artifacts + assert artifacts["reports/world_class_operator_runbook.md"]["exists"], artifacts + assert artifacts["reports/world_class_operator_runbook.html"]["exists"], artifacts + assert artifacts["reports/world_class_claim_guard.json"]["exists"], artifacts + assert artifacts["reports/python_compatibility.json"]["exists"], artifacts + assert any(command["command"] == "make ci-test" for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions" for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions" for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions" for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions" for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py world-class-claim-guard ." for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py python-compat ." for command in payload["reproduction_commands"]), payload + assert any(command["command"] == "python3 scripts/yao.py evidence-consistency ." for command in payload["reproduction_commands"]), payload + assert any("Provider-backed model holdout source evidence is complete" in item for item in payload["limitations"]), payload["limitations"] + markdown = output_md.read_text(encoding="utf-8") + assert "Benchmark Reproducibility" in markdown, markdown + assert "Evidence bundle SHA256" in markdown, markdown + assert "release lock ready" in markdown, markdown + assert "Source tree dirty at generation" in markdown, markdown + assert "Generated evidence dirty at generation" in markdown, markdown + assert "source changed files at generation" in markdown, markdown + assert "generated changed files at generation" in markdown, markdown + assert "world-class source checks" in markdown, markdown + assert "beta test ready" in markdown, markdown + assert "## Beta Test Boundary" in markdown, markdown + assert "human-adjudication" in markdown, markdown + assert "do not claim world-class" in markdown, markdown + assert "public claim ready: `false`" in markdown, markdown + assert "## Public Claim Boundary" in markdown, markdown + assert "provider-backed model holdout evidence is incomplete" not in markdown, markdown + assert "Provider-backed model holdout source evidence is complete" in markdown, markdown + assert "world-class source checks are not all accepted" in markdown, markdown + assert "## Release Lock" in markdown, markdown + assert "## Evidence Bundle" in markdown, markdown + assert "reports/benchmark_methodology.md" in markdown, markdown + assert "reports/world_class_evidence_preflight.json" in markdown, markdown + assert "reports/world_class_operator_runbook.html" in markdown, markdown + assert "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions" in markdown, markdown + assert "python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions" in markdown, markdown + assert "python3 scripts/yao.py evidence-consistency ." in markdown, markdown + assert "make ci-test" in markdown, markdown + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_compile_skill.py b/tests/verify_compile_skill.py new file mode 100644 index 0000000..6c3a0c2 --- /dev/null +++ b/tests/verify_compile_skill.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "compile_skill.py" +TRUST_SCRIPT = ROOT / "scripts" / "trust_check.py" + + +def run(*args: str, check: bool = True) -> subprocess.CompletedProcess: + return subprocess.run( + [sys.executable, str(SCRIPT), *args], + cwd=ROOT, + capture_output=True, + text=True, + check=check, + ) + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_compile_skill" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + output_json = tmp_root / "compiled_targets.json" + output_md = tmp_root / "compiled_targets.md" + subprocess.run( + [sys.executable, str(TRUST_SCRIPT), str(ROOT)], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + + proc = run( + str(ROOT), + "--target", + "openai", + "--target", + "claude", + "--target", + "generic", + "--target", + "vscode", + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-13", + ) + payload = json.loads(proc.stdout) + assert payload["ok"], payload + assert payload["schema_version"] == "1.0", payload + assert payload["summary"]["target_count"] == 4, payload + assert payload["summary"]["pass_count"] == 4, payload + assert output_json.exists(), output_json + assert output_md.exists(), output_md + assert "Compiled Targets" in output_md.read_text(encoding="utf-8") + by_target = {item["target"]: item for item in payload["targets"]} + for target in ("openai", "claude", "generic", "vscode"): + item = by_target[target] + assert item["compiler"]["name"] == "yao-skill-ir-compiler", item + assert item["compiler"]["source"] == "skill-ir", item + assert item["compiled_contract"]["name"] == "yao-meta-skill", item + assert item["compiled_contract"]["target"] == target, item + assert item["compiled_contract"]["trigger"]["description"], item + assert item["compiled_contract"]["resources"]["counts"]["references"] > 0, item + assert item["compiled_contract"]["eval_plan"]["counts"]["output"] > 0, item + assert item["compiled_contract"]["permissions"]["source"] == "reports/security_trust_report.json", item + assert "file_write" in item["compiled_contract"]["permissions"]["declared_capabilities"], item + assert item["compiled_contract"]["target_permission_contract"]["target"] == target, item + assert item["compiled_contract"]["target_permission_contract"]["representation"], item + assert item["permission_contract"]["help_smoke"]["failed_count"] == 0, item + assert item["target_permission_contract"]["target"] == target, item + assert item["target_permission_contract"]["capability_counts"]["file_write"] > 0, item + native = item["target_native_contract"] + assert native["target"] == target, item + assert native["native_surface"], item + assert native["activation"]["policy"], item + assert native["resources"]["strategy"], item + assert native["scripts"]["strategy"], item + assert native["permissions"]["enforcement"], item + assert native["review"]["artifacts"], item + assert item["compiled_contract"]["target_native_contract"]["target"] == target, item + assert item["target_transform"]["generated_files"], item + assert item["target_transform"]["metadata_mapping"], item + assert item["target_transform"]["permission_representation"], item + assert item["target_transform"]["native_surface"] == native["native_surface"], item + assert item["target_transform"]["activation_policy"] == native["activation"]["policy"], item + + unsupported = run(str(ROOT), "--target", "unsupported-target", "--output-json", str(tmp_root / "bad.json"), "--output-md", str(tmp_root / "bad.md"), check=False) + bad_payload = json.loads(unsupported.stdout) + assert unsupported.returncode == 2, unsupported.stdout + assert not bad_payload["ok"], bad_payload + assert bad_payload["summary"]["block_count"] == 1, bad_payload + assert bad_payload["failures"], bad_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_conformance_suite.py b/tests/verify_conformance_suite.py new file mode 100644 index 0000000..b180b16 --- /dev/null +++ b/tests/verify_conformance_suite.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "run_conformance_suite.py" +EXPORT_IR = ROOT / "scripts" / "export_skill_ir.py" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_conformance" + tmp_root.mkdir(parents=True, exist_ok=True) + output_json = tmp_root / "conformance_matrix.json" + output_md = tmp_root / "conformance_matrix.md" + subprocess.run( + [ + sys.executable, + str(EXPORT_IR), + str(ROOT), + "--output-json", + str(ROOT / "skill-ir" / "examples" / "yao-meta-skill.json"), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["ok"], payload + assert payload["summary"]["target_count"] == 5, payload + assert payload["summary"]["pass_count"] == 5, payload + assert {item["target"] for item in payload["targets"]} == {"openai", "claude", "agent-skills", "vscode", "generic"}, payload + assert output_json.exists(), output_json + assert output_md.exists(), output_md + assert "Runtime Conformance Matrix" in output_md.read_text(encoding="utf-8") + + renamed_workspace = tmp_root / "renamed-conformance-root" + shutil.rmtree(renamed_workspace, ignore_errors=True) + renamed_root = renamed_workspace / "checkout-alias" + shutil.copytree( + ROOT, + renamed_root, + ignore=shutil.ignore_patterns(".git", ".previews", "dist", "__pycache__", ".pytest_cache", "tmp*"), + ) + renamed_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(renamed_root), + "--output-json", + str(tmp_root / "renamed_conformance_matrix.json"), + "--output-md", + str(tmp_root / "renamed_conformance_matrix.md"), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + renamed_payload = json.loads(renamed_proc.stdout) + assert renamed_payload["ok"], renamed_payload + assert renamed_payload["summary"]["pass_count"] == 5, renamed_payload + warnings = [warning for target in renamed_payload["targets"] for warning in target["warnings"]] + assert any("source checkout directory differs" in warning for warning in warnings), renamed_payload + + broken = tmp_root / "broken-skill" + (broken / "agents").mkdir(parents=True, exist_ok=True) + (broken / "reports").mkdir(parents=True, exist_ok=True) + (broken / "SKILL.md").write_text("---\nname: broken-skill\n---\n\n# Broken\n", encoding="utf-8") + (broken / "manifest.json").write_text(json.dumps({"name": "broken-skill"}, indent=2), encoding="utf-8") + (broken / "agents" / "interface.yaml").write_text("interface: {}\ncompatibility: {}\n", encoding="utf-8") + broken_proc = subprocess.run( + [sys.executable, str(SCRIPT), str(broken), "--target", "generic"], + cwd=ROOT, + capture_output=True, + text=True, + ) + assert broken_proc.returncode == 2, broken_proc.stdout + broken_payload = json.loads(broken_proc.stdout) + assert not broken_payload["ok"], broken_payload + assert broken_payload["targets"][0]["failures"], broken_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_daily_skillops.py b/tests/verify_daily_skillops.py new file mode 100644 index 0000000..8c39851 --- /dev/null +++ b/tests/verify_daily_skillops.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_daily_skillops_report.py" +CLI = ROOT / "scripts" / "yao.py" +TMP = ROOT / "tests" / "tmp_daily_skillops" + + +def run_command(*args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + + +def write_json(path: Path, payload: dict) -> 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 seed_release_reports(skill_dir: Path) -> None: + reports = skill_dir / "reports" + write_json( + reports / "skill_os2_coverage.json", + { + "ok": True, + "summary": { + "local_blueprint_ready": True, + "public_world_class_ready": False, + "world_class_evidence_pending_count": 4, + }, + }, + ) + write_json( + reports / "world_class_evidence_ledger.json", + { + "ok": True, + "summary": { + "pending_count": 4, + "accepted_count": 0, + "ready_to_claim_world_class": False, + }, + }, + ) + write_json(reports / "evidence_consistency.json", {"ok": True, "checks": [{"status": "pass"}]}) + write_json(reports / "benchmark_reproducibility.json", {"ok": True, "summary": {"release_lock_ready": True}}) + write_json( + reports / "adaptation_approval_ledger.json", + { + "ok": True, + "summary": { + "approval_count": 1, + "pending_review_count": 1, + "applied_count": 0, + "rollback_count": 0, + }, + }, + ) + write_json( + reports / "adaptation_regression_report.json", + { + "ok": True, + "summary": { + "attempt_count": 1, + "dry_run_count": 1, + "applied_count": 0, + "rollback_count": 0, + }, + }, + ) + + +def write_signal_source(path: Path) -> None: + path.write_text( + "\n".join( + [ + json.dumps({"text": "报告默认中文简体,同时右上角提供英文切换;敏感内容 token=abc123456789、sk-1234567890abcdef 和 /Users/laoyao/private/path 必须脱敏。"}, ensure_ascii=False), + json.dumps({"message": "新的 HTML 报告要双语,但默认中文简体。"}, ensure_ascii=False), + json.dumps({"content": "报告 UI 需要 Kami 白底排版、图表模块和清晰导航。"}, ensure_ascii=False), + json.dumps({"excerpt": "HTML 报告还是白底 Kami 风格,图表不要挤在一起。"}, ensure_ascii=False), + json.dumps({"note": "不要自动扫描私人日志;必须由用户提供明确路径。"}, ensure_ascii=False), + json.dumps({"body": "自适应升级需要先输出提案,授权后再修改,并能回滚。"}, ensure_ascii=False), + json.dumps({"text": "每次升级都要有测试、证据报告和 GitHub push。"}, ensure_ascii=False), + json.dumps({"text": "敏感内容也不能直接进入公开日报。"}, ensure_ascii=False), + ] + ) + + "\n", + encoding="utf-8", + ) + + +def assert_daily_contract(payload: dict) -> None: + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + assert payload["report_contract"]["contract"] == "daily-skillops-report", payload + assert payload["report_contract"]["top_level_mirrors_summary"] is True, payload + for field in payload["report_contract"]["summary_fields"]: + assert payload[field] == payload["summary"][field], (field, payload.get(field), payload["summary"].get(field)) + contract = payload["operations_contract"] + assert contract["explicit_source_required_for_scan"] is True, payload + assert contract["implicit_private_log_scan"] is False, payload + assert contract["raw_content_stored"] is False, payload + assert contract["redacted_excerpts_only"] is True, payload + assert contract["proposal_only"] is True, payload + assert contract["approval_required_for_writes"] is True, payload + assert contract["writes_source_files"] is False, payload + assert contract["auto_patch_enabled"] is False, payload + assert contract["daily_report_counts_as_world_class_evidence"] is False, payload + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + skill_dir = TMP / "daily-skillops-demo" + skill_dir.mkdir(parents=True) + seed_release_reports(skill_dir) + source = TMP / "curated-signals.jsonl" + write_signal_source(source) + source_before = source.read_text(encoding="utf-8") + + output_json = skill_dir / "reports" / "skillops" / "daily" / "2026-06-16.json" + output_md = skill_dir / "reports" / "skillops" / "daily" / "2026-06-16.md" + proc = run_command( + str(SCRIPT), + str(skill_dir), + "--source", + str(source), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-16T08:00:00Z", + ) + assert proc.returncode == 0, proc.stderr + payload = json.loads(proc.stdout) + assert_daily_contract(payload) + assert payload["summary"]["source_supplied"] is True, payload + assert payload["summary"]["pattern_count"] >= 4, payload + assert payload["summary"]["proposal_count"] >= 4, payload + assert payload["summary"]["pending_review_count"] == 1, payload + assert payload["summary"]["world_class_pending_count"] == 4, payload + assert payload["summary"]["public_world_class_ready"] is False, payload + assert payload["summary"]["release_lock_ready"] is True, payload + assert payload["summary"]["evidence_consistency_ok"] is True, payload + assert payload["opportunity_summary"]["opportunity_count"] >= 4, payload + assert payload["opportunity_summary"]["top_score"] >= 70, payload + assert payload["decision_policy"]["writes_source_files"] is False, payload + assert payload["decision_policy"]["auto_patch_enabled"] is False, payload + action_types = {item["action_type"] for item in payload["opportunities"]} + assert {"patch_existing_skill", "agents_update"} <= action_types, payload["opportunities"] + assert action_types <= set(payload["decision_policy"]["action_types"]), payload["opportunities"] + assert all(item["write_allowed_without_approval"] is False for item in payload["opportunities"]), payload[ + "opportunities" + ] + action_keys = {item["key"] for item in payload["actions"]} + assert {"review-adaptation-proposals", "resolve-pending-approvals", "close-world-class-evidence"} <= action_keys, payload + assert output_json.exists(), output_json + assert output_md.exists(), output_md + assert (skill_dir / "reports" / "user_patterns.json").exists(), skill_dir + assert (skill_dir / "reports" / "adaptation_proposals.json").exists(), skill_dir + assert source.read_text(encoding="utf-8") == source_before, source + + serialized = json.dumps(payload, ensure_ascii=False) + assert "sk-1234567890abcdef" not in serialized, serialized + assert "token=abc123456789" not in serialized, serialized + assert "/Users/laoyao/private" not in serialized, serialized + assert "[REDACTED_SECRET]" in serialized, serialized + assert "[LOCAL_PATH]" in serialized, serialized + markdown = output_md.read_text(encoding="utf-8") + assert "Daily SkillOps Report" in markdown, markdown + assert "does not scan private logs" in markdown, markdown + assert "daily_report_counts_as_world_class_evidence: `false`" in markdown, markdown + assert "## Opportunities" in markdown, markdown + assert "ready for approval review" in markdown, markdown + + cli_dir = TMP / "cli-daily-skillops-demo" + cli_dir.mkdir(parents=True) + seed_release_reports(cli_dir) + cli_output_json = cli_dir / "reports" / "skillops" / "daily" / "cli.json" + cli_output_md = cli_dir / "reports" / "skillops" / "daily" / "cli.md" + cli_proc = run_command( + str(CLI), + "daily-skillops", + str(cli_dir), + "--source", + str(source), + "--output-json", + str(cli_output_json), + "--output-md", + str(cli_output_md), + "--generated-at", + "2026-06-16T08:00:00Z", + ) + assert cli_proc.returncode == 0, cli_proc.stderr + cli_payload = json.loads(cli_proc.stdout) + assert_daily_contract(cli_payload) + assert cli_payload["summary"]["proposal_count"] >= 4, cli_payload + assert cli_output_json.exists(), cli_output_json + assert cli_output_md.exists(), cli_output_md + + history_source = TMP / ".zsh_history" + history_source.write_text("报告默认中文\n报告默认中文\n", encoding="utf-8") + history_proc = run_command(str(SCRIPT), str(skill_dir), "--source", str(history_source)) + assert history_proc.returncode == 2, history_proc.stdout + history_payload = json.loads(history_proc.stdout) + assert history_payload["ok"] is False, history_payload + assert any("Refusing private history source" in item for item in history_payload["failures"]), history_payload + + no_refresh_dir = TMP / "no-refresh-daily-skillops-demo" + no_refresh_dir.mkdir(parents=True) + seed_release_reports(no_refresh_dir) + write_json( + no_refresh_dir / "reports" / "user_patterns.json", + {"ok": True, "summary": {"pattern_count": 0}, "patterns": []}, + ) + no_refresh_output_json = no_refresh_dir / "reports" / "skillops" / "daily" / "no-refresh.json" + no_refresh_proc = run_command( + str(SCRIPT), + str(no_refresh_dir), + "--source", + str(source), + "--no-refresh-source-reports", + "--output-json", + str(no_refresh_output_json), + "--generated-at", + "2026-06-16T08:00:00Z", + ) + assert no_refresh_proc.returncode == 0, no_refresh_proc.stderr + no_refresh_payload = json.loads(no_refresh_proc.stdout) + assert_daily_contract(no_refresh_payload) + assert no_refresh_payload["summary"]["source_supplied"] is True, no_refresh_payload + assert no_refresh_payload["summary"]["pattern_count"] >= 4, no_refresh_payload + assert no_refresh_payload["summary"]["proposal_count"] >= 4, no_refresh_payload + assert no_refresh_payload["opportunity_summary"]["opportunity_count"] >= 4, no_refresh_payload + assert not (no_refresh_dir / "reports" / "adaptation_proposals.json").exists(), no_refresh_dir + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_description_optimization.py b/tests/verify_description_optimization.py new file mode 100644 index 0000000..fe5c37a --- /dev/null +++ b/tests/verify_description_optimization.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +CASES = [ + { + "name": "root", + "description_file": ROOT / "SKILL.md", + "baseline_description_file": ROOT / "evals" / "baseline_description.txt", + "semantic_config": ROOT / "evals" / "semantic_config.json", + "dev_cases": ROOT / "evals" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "evals" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "evals" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "evals" / "adversarial" / "trigger_cases.json", + }, + { + "name": "team_frontend_review", + "description_file": ROOT / "examples" / "team-frontend-review" / "generated-skill" / "SKILL.md", + "baseline_description_file": ROOT / "examples" / "team-frontend-review" / "optimization" / "baseline_description.txt", + "semantic_config": ROOT / "examples" / "team-frontend-review" / "optimization" / "semantic_config.json", + "dev_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "examples" / "team-frontend-review" / "optimization" / "adversarial" / "trigger_cases.json", + }, + { + "name": "governed_incident_command", + "description_file": ROOT / "examples" / "governed-incident-command" / "generated-skill" / "SKILL.md", + "baseline_description_file": ROOT / "examples" / "governed-incident-command" / "optimization" / "baseline_description.txt", + "semantic_config": ROOT / "examples" / "governed-incident-command" / "optimization" / "semantic_config.json", + "dev_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "dev" / "trigger_cases.json", + "holdout_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "holdout" / "trigger_cases.json", + "blind_holdout_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "blind_holdout" / "trigger_cases.json", + "adversarial_cases": ROOT / "examples" / "governed-incident-command" / "optimization" / "adversarial" / "trigger_cases.json", + }, +] + + +def total_errors(summary: dict) -> int: + return int(summary.get("false_positives", 0)) + int(summary.get("false_negatives", 0)) + + +def run_case(case: dict) -> dict: + proc = subprocess.run( + [ + sys.executable, + "scripts/optimize_description.py", + "--description-file", + str(case["description_file"]), + "--baseline-description-file", + str(case["baseline_description_file"]), + "--semantic-config", + str(case["semantic_config"]), + "--dev-cases", + str(case["dev_cases"]), + "--holdout-cases", + str(case["holdout_cases"]), + "--blind-holdout-cases", + str(case["blind_holdout_cases"]), + "--adversarial-cases", + str(case["adversarial_cases"]), + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + current = payload["current_candidate"] + winner = payload["winner"] + baseline = payload.get("baseline") + passed = proc.returncode == 0 + passed = passed and total_errors(winner["dev"]) <= total_errors(current["dev"]) + passed = passed and total_errors(winner["holdout"]) <= total_errors(current["holdout"]) + if baseline: + passed = passed and total_errors(winner["dev"]) <= total_errors(baseline["dev"]) + passed = passed and total_errors(winner["holdout"]) <= total_errors(baseline["holdout"]) + blind_winner = payload["acceptance_gates"]["blind_holdout_non_regression"]["winner"] + blind_current = payload["acceptance_gates"]["blind_holdout_non_regression"]["current"] + blind_baseline = payload["acceptance_gates"]["blind_holdout_non_regression"]["baseline"] + passed = passed and blind_winner is not None and total_errors(blind_winner) <= total_errors(blind_current) + if blind_baseline: + passed = passed and total_errors(blind_winner) <= total_errors(blind_baseline) + judge_blind_winner = payload["acceptance_gates"]["judge_blind_holdout_non_regression"]["winner"] + judge_blind_current = payload["acceptance_gates"]["judge_blind_holdout_non_regression"]["current"] + judge_blind_baseline = payload["acceptance_gates"]["judge_blind_holdout_non_regression"]["baseline"] + passed = passed and judge_blind_winner is not None and total_errors(judge_blind_winner) <= total_errors(judge_blind_current) + if judge_blind_baseline: + passed = passed and total_errors(judge_blind_winner) <= total_errors(judge_blind_baseline) + adversarial_winner = payload["acceptance_gates"]["adversarial_holdout_non_regression"]["winner"] + adversarial_current = payload["acceptance_gates"]["adversarial_holdout_non_regression"]["current"] + adversarial_baseline = payload["acceptance_gates"]["adversarial_holdout_non_regression"]["baseline"] + passed = passed and adversarial_winner is not None and total_errors(adversarial_winner) <= total_errors(adversarial_current) + if adversarial_baseline: + passed = passed and total_errors(adversarial_winner) <= total_errors(adversarial_baseline) + winner_adversarial_calibration = payload["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_calibration"] + winner_adversarial_family = payload["acceptance_gates"]["adversarial_holdout_non_regression"]["winner_family_health"] + winner_judge_summary = (judge_blind_winner or {}).get("judge_summary") or {} + winner_judge_family = payload["acceptance_gates"]["judge_blind_holdout_non_regression"]["winner_family_health"] + passed = passed and winner_adversarial_calibration is not None + passed = passed and winner_adversarial_family is not None + passed = passed and winner_adversarial_family.get("family_count", 0) >= 2 + passed = passed and winner_judge_summary.get("agreement_rate") is not None + passed = passed and winner_judge_family is not None + passed = passed and winner_judge_family.get("family_count", 0) >= 2 + passed = passed and len(payload.get("candidates", [])) >= 3 + return { + "name": case["name"], + "passed": passed, + "returncode": proc.returncode, + "summary": payload["summary"], + "winner_label": winner["label"], + "winner_tokens": winner["estimated_tokens"], + "current_tokens": current["estimated_tokens"], + "baseline_tokens": baseline["estimated_tokens"] if baseline else None, + "winner_blind_holdout_total_errors": total_errors(blind_winner) if blind_winner else None, + "winner_judge_blind_holdout_total_errors": total_errors(judge_blind_winner) if judge_blind_winner else None, + "winner_judge_blind_agreement_rate": winner_judge_summary.get("agreement_rate"), + "winner_adversarial_holdout_total_errors": total_errors(adversarial_winner) if adversarial_winner else None, + "winner_adversarial_risk_band": winner_adversarial_calibration.get("risk_band") if winner_adversarial_calibration else None, + } + + +def main() -> None: + results = [run_case(case) for case in CASES] + report = {"ok": all(result["passed"] for result in results), "cases": results} + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_evidence_consistency.py b/tests/verify_evidence_consistency.py new file mode 100644 index 0000000..d420960 --- /dev/null +++ b/tests/verify_evidence_consistency.py @@ -0,0 +1,719 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "scripts")) +from evidence_consistency_release import CLEAN_LOCK_REPORT_COMMANDS, SOURCE_REFRESH_REPORT_COMMANDS + +SCRIPT = ROOT / "scripts" / "render_evidence_consistency.py" +TMP = ROOT / "tests" / "tmp_evidence_consistency" +REPORT_FILES = [ + "AGENTS.md", + "reports/benchmark_reproducibility.json", + "reports/skill-overview.json", + "reports/skill-overview.html", + "reports/skill-interpretation.json", + "reports/skill-interpretation.html", + "reports/adoption_drift_report.json", + "reports/world_class_evidence_ledger.json", + "reports/world_class_evidence_plan.json", + "reports/world_class_evidence_intake.json", + "reports/world_class_evidence_preflight.json", + "reports/world_class_evidence_preflight.html", + "reports/world_class_submission_review.json", + "reports/world_class_operator_runbook.json", + "reports/skill_os2_coverage.json", + "reports/review-studio.json", + "reports/package_verification.json", + "reports/install_simulation.json", + "reports/security_trust_report.json", + "reports/context_budget.json", + "reports/world_class_claim_guard.json", + "reports/skill-os-2-review.md", + "skill-ir/examples/yao-meta-skill.json", + "scripts/ci_test.py", +] + + +def run(cmd: list[str], *, check: bool = False) -> subprocess.CompletedProcess[str]: + return subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True, check=check) + + +def copy_reports(dst: Path) -> None: + for relative in REPORT_FILES: + source = ROOT / relative + target = dst / relative + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(source, target) + + +def refresh_embedded_reports() -> None: + script_names = [ + "render_context_reports.py", + "render_skill_os2_coverage.py", + "render_world_class_evidence_plan.py", + "render_world_class_evidence_ledger.py", + "render_world_class_evidence_intake.py", + "render_world_class_preflight.py", + "render_world_class_submission_review.py", + "render_world_class_operator_runbook.py", + "render_world_class_claim_guard.py", + "render_benchmark_reproducibility.py", + "render_skill_overview.py", + "render_skill_interpretation.py", + "render_review_viewer.py", + "render_review_studio.py", + ] + allow_nonzero = { + "render_world_class_evidence_intake.py", + "render_world_class_submission_review.py", + } + for script_name in script_names: + command = [sys.executable, str(ROOT / "scripts" / script_name)] + if script_name != "render_context_reports.py": + command.append(str(ROOT)) + proc = subprocess.run( + command, + cwd=ROOT, + capture_output=True, + text=True, + ) + if proc.returncode != 0 and script_name not in allow_nonzero: + raise subprocess.CalledProcessError(proc.returncode, command, proc.stdout, proc.stderr) + + +def assert_world_class_roadmap_matches_ledger() -> None: + ledger = json.loads((ROOT / "reports" / "world_class_evidence_ledger.json").read_text(encoding="utf-8")) + summary = ledger["summary"] + pending_count = int(summary["pending_count"]) + external_pending_count = int(summary["external_pending_count"]) + human_pending_count = int(summary["human_pending_count"]) + expected_total = f"{pending_count} 项待补证据" + expected_breakdown = f"外部 {external_pending_count} 项、人工 {human_pending_count} 项" + + for report_name in ["skill-overview", "skill-interpretation"]: + report_path = ROOT / "reports" / f"{report_name}.json" + report = json.loads(report_path.read_text(encoding="utf-8")) + serialized = json.dumps(report, ensure_ascii=False) + assert "继续补齐剩余 2 项外部/人工证据" not in serialized, report_name + assert report["world_class_readiness"]["pending_count"] == pending_count, report["world_class_readiness"] + assert report["world_class_readiness"]["external_pending_count"] == external_pending_count, report[ + "world_class_readiness" + ] + assert report["world_class_readiness"]["human_pending_count"] == human_pending_count, report[ + "world_class_readiness" + ] + actions = "\n".join(report["iteration_roadmap"]["items"][0]["actions"]) + if pending_count > 2: + assert expected_total in actions, actions + assert expected_breakdown in actions, actions + + +def assert_release_evidence_instructions_cover_first_class_reports() -> None: + agents_text = (ROOT / "AGENTS.md").read_text(encoding="utf-8") + source_refresh_header = "After source changes that affect scripts" + clean_lock_header = "For final release evidence" + assert source_refresh_header in agents_text, agents_text + assert clean_lock_header in agents_text, agents_text + + source_refresh = agents_text.split(source_refresh_header, 1)[1].split(clean_lock_header, 1)[0] + clean_lock = agents_text.split(clean_lock_header, 1)[1].split("If `reports/benchmark_reproducibility.json`", 1)[0] + for command in SOURCE_REFRESH_REPORT_COMMANDS: + assert command in source_refresh, command + for command in CLEAN_LOCK_REPORT_COMMANDS: + assert command in clean_lock, command + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + refresh_embedded_reports() + assert_world_class_roadmap_matches_ledger() + assert_release_evidence_instructions_cover_first_class_reports() + output_json = TMP / "evidence_consistency.json" + output_md = TMP / "evidence_consistency.md" + proc = run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-15", + ], + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + assert payload["summary"]["decision"] == "consistent", payload + assert payload["summary"]["fail_count"] == 0, payload + assert payload["summary"]["check_count"] >= 31, payload + checks = {item["key"]: item for item in payload["checks"]} + assert checks["release-evidence-flow-covers-first-class-reports"]["status"] == "pass", checks[ + "release-evidence-flow-covers-first-class-reports" + ] + assert checks["review-studio-context-budget-mirror"]["status"] == "pass", checks[ + "review-studio-context-budget-mirror" + ] + assert checks["skill-ir-evidence-path-contract"]["status"] == "pass", checks[ + "skill-ir-evidence-path-contract" + ] + assert checks["skill-ir-evidence-path-contract"]["actual"]["review_studio_evidence_path"] == ( + "skill-ir/examples/yao-meta-skill.json" + ), checks["skill-ir-evidence-path-contract"] + assert checks["review-studio-gate-action-mirror"]["status"] == "pass", checks[ + "review-studio-gate-action-mirror" + ] + gate_action_mirror = checks["review-studio-gate-action-mirror"]["actual"] + assert gate_action_mirror["non_pass_gate_keys"] == [ + "output-lab", + "review-waivers", + "world-class-evidence", + ], gate_action_mirror + assert gate_action_mirror["action_gate_keys"] == gate_action_mirror["non_pass_gate_keys"], gate_action_mirror + assert gate_action_mirror["pass_gate_action_ids_empty"] is True, gate_action_mirror + assert gate_action_mirror["gate_action_mirrors"]["world-class-evidence"]["source_ref_present"] is True, ( + gate_action_mirror + ) + assert gate_action_mirror["gate_action_mirrors"]["world-class-evidence"]["title_present"] is True, ( + gate_action_mirror + ) + assert gate_action_mirror["gate_action_mirrors"]["world-class-evidence"]["next_step_present"] is True, ( + gate_action_mirror + ) + assert gate_action_mirror["gate_action_mirrors"]["world-class-evidence"]["reason_present"] is True, ( + gate_action_mirror + ) + assert checks["overview-benchmark-summary"]["status"] == "pass", checks["overview-benchmark-summary"] + assert checks["interpretation-adoption-summary"]["status"] == "pass", checks["interpretation-adoption-summary"] + assert checks["coverage-world-class-boundary"]["status"] == "pass", checks["coverage-world-class-boundary"] + assert checks["benchmark-beta-public-claim-split"]["status"] == "pass", checks[ + "benchmark-beta-public-claim-split" + ] + beta_split = checks["benchmark-beta-public-claim-split"]["actual"] + assert beta_split["public_claim_ready"] is False, beta_split + assert beta_split["human_review_complete"] is False, beta_split + assert beta_split["deferred_evidence_keys"] == ["human-adjudication", "native-client-telemetry", "native-permission-enforcement", "provider-holdout"], beta_split + assert beta_split["deferred_human_review"] is True, beta_split + assert checks["preflight-world-class-boundary"]["status"] == "pass", checks["preflight-world-class-boundary"] + assert checks["preflight-submission-kit-handoff"]["status"] == "pass", checks[ + "preflight-submission-kit-handoff" + ] + preflight_handoff = checks["preflight-submission-kit-handoff"]["actual"] + assert preflight_handoff["artifact_role_source"] == "world-class-submission-kit", preflight_handoff + assert preflight_handoff["artifact_role_counts_as_evidence"] is False, preflight_handoff + assert preflight_handoff["submission_ref_role_present"] is True, preflight_handoff + assert preflight_handoff["supporting_evidence_role_present"] is True, preflight_handoff + assert preflight_handoff["submission_ref_copy_to_artifact_refs"] is True, preflight_handoff + assert preflight_handoff["supporting_evidence_copy_to_artifact_refs"] is False, preflight_handoff + assert checks["review-studio-preflight-artifact-role-handoff"]["status"] == "pass", checks[ + "review-studio-preflight-artifact-role-handoff" + ] + assert checks["world-class-phase-queue-consistency"]["status"] == "pass", checks[ + "world-class-phase-queue-consistency" + ] + phase_queue_actual = checks["world-class-phase-queue-consistency"]["actual"] + assert phase_queue_actual["summary"]["phase_queue_count"] == 2, phase_queue_actual + assert phase_queue_actual["summary"]["phase_queue_row_count"] >= 11, phase_queue_actual + assert phase_queue_actual["summary"] == phase_queue_actual["operator_runbook_summary"], phase_queue_actual + assert phase_queue_actual["top_level_phase_queue"] == phase_queue_actual[ + "operator_runbook_top_level_phase_queue" + ], phase_queue_actual + assert phase_queue_actual["phase_queue_counts_as_completion"] is False, phase_queue_actual + assert set(phase_queue_actual["item_phase_queues"]) == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, phase_queue_actual + assert phase_queue_actual["operator_runbook_phase_queues"] == phase_queue_actual["item_phase_queues"], ( + phase_queue_actual + ) + assert phase_queue_actual["review_studio_phase_queues"] == phase_queue_actual["item_phase_queues"], ( + phase_queue_actual + ) + role_handoff = checks["review-studio-preflight-artifact-role-handoff"]["actual"] + assert role_handoff["provider-holdout"]["role_source"] == "world-class-submission-kit", role_handoff + assert role_handoff["provider-holdout"]["submission_ref_total_count"] == 1, role_handoff + assert role_handoff["provider-holdout"]["submission_ref_copy_to_artifact_refs"] is True, role_handoff + assert role_handoff["provider-holdout"]["supporting_evidence_copy_to_artifact_refs"] is False, role_handoff + assert checks["review-studio-no-overclaim"]["status"] == "pass", checks["review-studio-no-overclaim"] + assert checks["claim-guard-package-runtime-surface"]["status"] == "pass", checks[ + "claim-guard-package-runtime-surface" + ] + assert checks["world-class-evidence-workflow-coverage"]["status"] == "pass", checks[ + "world-class-evidence-workflow-coverage" + ] + workflow_actual = checks["world-class-evidence-workflow-coverage"]["actual"] + assert workflow_actual["operator_coordination_keys"] == [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout", + ], workflow_actual + assert workflow_actual["operator_coordination_counts_as_completion"] is False, workflow_actual + assert workflow_actual["operator_release_gate_ready"] is False, workflow_actual + assert workflow_actual["operator_release_gate_counts_as_completion"] is False, workflow_actual + assert checks["skill-os-2-review-current-evidence"]["status"] == "pass", checks[ + "skill-os-2-review-current-evidence" + ] + markdown = output_md.read_text(encoding="utf-8") + assert "Evidence Consistency" in markdown, markdown + assert "decision: `consistent`" in markdown, markdown + assert "does not create provider, human, native-client, or permission-enforcement evidence" in markdown, markdown + + drift_root = TMP / "drift-skill" + copy_reports(drift_root) + overview_path = drift_root / "reports" / "skill-overview.json" + overview = json.loads(overview_path.read_text(encoding="utf-8")) + overview["adoption_drift"]["summary"]["adoption_sample_count"] += 1 + overview_path.write_text(json.dumps(overview, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(drift_root), + "--output-json", + str(TMP / "drift.json"), + "--output-md", + str(TMP / "drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert drift_proc.returncode == 2, drift_proc.stdout + drift_payload = json.loads(drift_proc.stdout) + drift_checks = {item["key"]: item for item in drift_payload["checks"]} + assert drift_payload["ok"] is False, drift_payload + assert drift_payload["summary"]["decision"] == "evidence-drift-detected", drift_payload + assert drift_checks["overview-adoption-summary"]["status"] == "fail", drift_checks["overview-adoption-summary"] + assert drift_checks["interpretation-adoption-summary"]["status"] == "pass", drift_checks["interpretation-adoption-summary"] + + skill_ir_drift_root = TMP / "skill-ir-drift-skill" + copy_reports(skill_ir_drift_root) + studio_path = skill_ir_drift_root / "reports" / "review-studio.json" + studio = json.loads(studio_path.read_text(encoding="utf-8")) + studio["evidence_paths"]["skill_ir"] = "reports/skill-ir.json" + studio_path.write_text(json.dumps(studio, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + skill_ir_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(skill_ir_drift_root), + "--output-json", + str(TMP / "skill_ir_drift.json"), + "--output-md", + str(TMP / "skill_ir_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert skill_ir_drift_proc.returncode == 2, skill_ir_drift_proc.stdout + skill_ir_drift_payload = json.loads(skill_ir_drift_proc.stdout) + skill_ir_drift_checks = {item["key"]: item for item in skill_ir_drift_payload["checks"]} + assert skill_ir_drift_checks["skill-ir-evidence-path-contract"]["status"] == "fail", ( + skill_ir_drift_checks["skill-ir-evidence-path-contract"] + ) + + claim_guard_drift_root = TMP / "claim-guard-drift-skill" + copy_reports(claim_guard_drift_root) + claim_guard_path = claim_guard_drift_root / "reports" / "world_class_claim_guard.json" + claim_guard = json.loads(claim_guard_path.read_text(encoding="utf-8")) + claim_guard["summary"]["package_claim_surface_count"] = 0 + claim_guard["scanned_surfaces"] = [ + item for item in claim_guard["scanned_surfaces"] if item.get("path") != "dist/manifest.json" + ] + claim_guard_path.write_text(json.dumps(claim_guard, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + claim_guard_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(claim_guard_drift_root), + "--output-json", + str(TMP / "claim_guard_drift.json"), + "--output-md", + str(TMP / "claim_guard_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert claim_guard_drift_proc.returncode == 2, claim_guard_drift_proc.stdout + claim_guard_drift_payload = json.loads(claim_guard_drift_proc.stdout) + claim_guard_drift_checks = {item["key"]: item for item in claim_guard_drift_payload["checks"]} + assert claim_guard_drift_checks["claim-guard-package-runtime-surface"]["status"] == "fail", ( + claim_guard_drift_checks["claim-guard-package-runtime-surface"] + ) + + preflight_drift_root = TMP / "preflight-drift-skill" + copy_reports(preflight_drift_root) + preflight_path = preflight_drift_root / "reports" / "world_class_evidence_preflight.json" + preflight = json.loads(preflight_path.read_text(encoding="utf-8")) + preflight["summary"]["credential_value_exposed"] = True + preflight_path.write_text(json.dumps(preflight, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + preflight_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(preflight_drift_root), + "--output-json", + str(TMP / "preflight_drift.json"), + "--output-md", + str(TMP / "preflight_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert preflight_drift_proc.returncode == 2, preflight_drift_proc.stdout + preflight_drift_payload = json.loads(preflight_drift_proc.stdout) + preflight_drift_checks = {item["key"]: item for item in preflight_drift_payload["checks"]} + assert preflight_drift_checks["preflight-world-class-boundary"]["status"] == "fail", ( + preflight_drift_checks["preflight-world-class-boundary"] + ) + + preflight_handoff_drift_root = TMP / "preflight-handoff-drift-skill" + copy_reports(preflight_handoff_drift_root) + preflight_handoff_path = preflight_handoff_drift_root / "reports" / "world_class_evidence_preflight.json" + preflight_handoff = json.loads(preflight_handoff_path.read_text(encoding="utf-8")) + preflight_handoff["submissions"]["drafts_count_as_evidence"] = True + preflight_handoff["submissions"]["commands"].pop("prepare_submission", None) + preflight_handoff_path.write_text(json.dumps(preflight_handoff, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + preflight_handoff_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(preflight_handoff_drift_root), + "--output-json", + str(TMP / "preflight_handoff_drift.json"), + "--output-md", + str(TMP / "preflight_handoff_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert preflight_handoff_drift_proc.returncode == 2, preflight_handoff_drift_proc.stdout + preflight_handoff_drift_payload = json.loads(preflight_handoff_drift_proc.stdout) + preflight_handoff_drift_checks = {item["key"]: item for item in preflight_handoff_drift_payload["checks"]} + assert preflight_handoff_drift_checks["preflight-submission-kit-handoff"]["status"] == "fail", ( + preflight_handoff_drift_checks["preflight-submission-kit-handoff"] + ) + + phase_queue_drift_root = TMP / "phase-queue-drift-skill" + copy_reports(phase_queue_drift_root) + phase_queue_path = phase_queue_drift_root / "reports" / "world_class_evidence_preflight.json" + phase_queue_payload = json.loads(phase_queue_path.read_text(encoding="utf-8")) + phase_queue_payload["summary"]["phase_queue_row_count"] = 1 + phase_queue_payload["phase_queue"][0]["counts_as_completion"] = True + phase_queue_payload["items"][0]["phase_queue"][0]["row_count"] = 999 + phase_queue_path.write_text(json.dumps(phase_queue_payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + operator_phase_queue_path = phase_queue_drift_root / "reports" / "world_class_operator_runbook.json" + operator_phase_queue_payload = json.loads(operator_phase_queue_path.read_text(encoding="utf-8")) + operator_phase_queue_payload["summary"]["phase_queue_count"] = 99 + operator_phase_queue_payload["phase_queue"][0]["row_count"] = 999 + operator_phase_queue_payload["items"][0]["phase_queue"][0]["row_count"] = 999 + operator_phase_queue_path.write_text( + json.dumps(operator_phase_queue_payload, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + phase_queue_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(phase_queue_drift_root), + "--output-json", + str(TMP / "phase_queue_drift.json"), + "--output-md", + str(TMP / "phase_queue_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert phase_queue_drift_proc.returncode == 2, phase_queue_drift_proc.stdout + phase_queue_drift_payload = json.loads(phase_queue_drift_proc.stdout) + phase_queue_drift_checks = {item["key"]: item for item in phase_queue_drift_payload["checks"]} + assert phase_queue_drift_checks["world-class-phase-queue-consistency"]["status"] == "fail", ( + phase_queue_drift_checks["world-class-phase-queue-consistency"] + ) + + gate_action_drift_root = TMP / "gate-action-drift-skill" + copy_reports(gate_action_drift_root) + gate_action_path = gate_action_drift_root / "reports" / "review-studio.json" + gate_action_payload = json.loads(gate_action_path.read_text(encoding="utf-8")) + for gate in gate_action_payload["gates"]: + if gate["key"] == "world-class-evidence": + gate["review_action_id"] = "" + gate["review_action_title"] = "" + gate["review_action_next_step"] = "" + gate["review_action_reason"] = "" + gate["review_action_source_ref_count"] = 0 + for action in gate_action_payload["review_actions"]: + if action["gate_key"] == "world-class-evidence": + action["title"] = "" + action["next_step"] = "" + action["reason"] = "" + action["source_refs"] = [] + gate_action_path.write_text(json.dumps(gate_action_payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + gate_action_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(gate_action_drift_root), + "--output-json", + str(TMP / "gate_action_drift.json"), + "--output-md", + str(TMP / "gate_action_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert gate_action_drift_proc.returncode == 2, gate_action_drift_proc.stdout + gate_action_drift_payload = json.loads(gate_action_drift_proc.stdout) + gate_action_drift_checks = {item["key"]: item for item in gate_action_drift_payload["checks"]} + assert gate_action_drift_checks["review-studio-gate-action-mirror"]["status"] == "fail", ( + gate_action_drift_checks["review-studio-gate-action-mirror"] + ) + + release_flow_drift_root = TMP / "release-flow-drift-skill" + copy_reports(release_flow_drift_root) + agents_path = release_flow_drift_root / "AGENTS.md" + agents_text = agents_path.read_text(encoding="utf-8") + agents_path.write_text( + agents_text.replace("python3 scripts/render_skill_interpretation.py .\n", "", 1), + encoding="utf-8", + ) + release_flow_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(release_flow_drift_root), + "--output-json", + str(TMP / "release_flow_drift.json"), + "--output-md", + str(TMP / "release_flow_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert release_flow_drift_proc.returncode == 2, release_flow_drift_proc.stdout + release_flow_drift_payload = json.loads(release_flow_drift_proc.stdout) + release_flow_drift_checks = {item["key"]: item for item in release_flow_drift_payload["checks"]} + assert release_flow_drift_checks["release-evidence-flow-covers-first-class-reports"]["status"] == "fail", ( + release_flow_drift_checks["release-evidence-flow-covers-first-class-reports"] + ) + + context_drift_root = TMP / "context-drift-skill" + copy_reports(context_drift_root) + context_path = context_drift_root / "reports" / "context_budget.json" + context_payload = json.loads(context_path.read_text(encoding="utf-8")) + context_payload["warnings"] = ["Deferred resource footprint is high: stale test warning."] + context_payload["stats"]["deferred_resource_tokens"] = 1 + context_payload["stats"]["deferred_resource_governance"]["status"] = "pass" + context_path.write_text(json.dumps(context_payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + context_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(context_drift_root), + "--output-json", + str(TMP / "context_drift.json"), + "--output-md", + str(TMP / "context_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert context_drift_proc.returncode == 2, context_drift_proc.stdout + context_drift_payload = json.loads(context_drift_proc.stdout) + context_drift_checks = {item["key"]: item for item in context_drift_payload["checks"]} + assert context_drift_checks["review-studio-context-budget-mirror"]["status"] == "fail", ( + context_drift_checks["review-studio-context-budget-mirror"] + ) + + benchmark_flow_drift_root = TMP / "benchmark-flow-drift-skill" + copy_reports(benchmark_flow_drift_root) + agents_path = benchmark_flow_drift_root / "AGENTS.md" + agents_text = agents_path.read_text(encoding="utf-8") + clean_lock_header = "For final release evidence" + benchmark_command = 'python3 scripts/render_benchmark_reproducibility.py . --generated-at "$GENERATED_AT"\n' + prefix, suffix = agents_text.split(clean_lock_header, 1) + agents_path.write_text( + prefix + clean_lock_header + suffix.replace(benchmark_command, "", 1), + encoding="utf-8", + ) + benchmark_flow_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(benchmark_flow_drift_root), + "--output-json", + str(TMP / "benchmark_flow_drift.json"), + "--output-md", + str(TMP / "benchmark_flow_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert benchmark_flow_drift_proc.returncode == 2, benchmark_flow_drift_proc.stdout + benchmark_flow_drift_payload = json.loads(benchmark_flow_drift_proc.stdout) + benchmark_flow_drift_checks = {item["key"]: item for item in benchmark_flow_drift_payload["checks"]} + assert benchmark_flow_drift_checks["release-evidence-flow-covers-first-class-reports"]["status"] == "fail", ( + benchmark_flow_drift_checks["release-evidence-flow-covers-first-class-reports"] + ) + + stale_clean_lock_root = TMP / "stale-clean-lock-skill" + copy_reports(stale_clean_lock_root) + benchmark_path = stale_clean_lock_root / "reports" / "benchmark_reproducibility.json" + benchmark = json.loads(benchmark_path.read_text(encoding="utf-8")) + benchmark["git_status"]["dirty"] = True + benchmark["git_status"]["changed_file_count"] = 3 + benchmark["git_status"]["source_dirty"] = True + benchmark["git_status"]["source_changed_file_count"] = 1 + benchmark["git_status"]["generated_dirty"] = True + benchmark["git_status"]["generated_changed_file_count"] = 2 + benchmark["summary"]["release_lock_ready"] = False + benchmark["summary"]["working_tree_dirty"] = True + benchmark["summary"]["changed_file_count"] = 3 + benchmark["summary"]["source_tree_dirty"] = True + benchmark["summary"]["source_changed_file_count"] = 1 + benchmark["summary"]["generated_tree_dirty"] = True + benchmark["summary"]["generated_changed_file_count"] = 2 + benchmark["release_lock"]["ready"] = False + benchmark["release_lock"]["reason"] = "source files were dirty at generation time" + benchmark_path.write_text(json.dumps(benchmark, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + for report_name in ["skill-overview", "skill-interpretation"]: + report_path = stale_clean_lock_root / "reports" / f"{report_name}.json" + report = json.loads(report_path.read_text(encoding="utf-8")) + report["benchmark_reproducibility"]["summary"]["release_lock_ready"] = False + report["benchmark_reproducibility"]["summary"]["working_tree_dirty"] = True + report["benchmark_reproducibility"]["summary"]["changed_file_count"] = 3 + report["benchmark_reproducibility"]["summary"]["source_tree_dirty"] = True + report["benchmark_reproducibility"]["summary"]["source_changed_file_count"] = 1 + report["benchmark_reproducibility"]["summary"]["generated_tree_dirty"] = True + report["benchmark_reproducibility"]["summary"]["generated_changed_file_count"] = 2 + report_path.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + subprocess.run(["git", "init"], cwd=stale_clean_lock_root, capture_output=True, text=True, check=True) + subprocess.run(["git", "add", "."], cwd=stale_clean_lock_root, capture_output=True, text=True, check=True) + subprocess.run( + ["git", "-c", "user.name=Yao Test", "-c", "user.email=yao-test@example.com", "commit", "-m", "seed"], + cwd=stale_clean_lock_root, + capture_output=True, + text=True, + check=True, + ) + stale_clean_lock_proc = run( + [ + sys.executable, + str(SCRIPT), + str(stale_clean_lock_root), + "--output-json", + str(TMP / "stale_clean_lock.json"), + "--output-md", + str(TMP / "stale_clean_lock.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert stale_clean_lock_proc.returncode == 2, stale_clean_lock_proc.stdout + stale_clean_lock_payload = json.loads(stale_clean_lock_proc.stdout) + stale_clean_lock_checks = {item["key"]: item for item in stale_clean_lock_payload["checks"]} + assert stale_clean_lock_checks["benchmark-release-lock-self-consistency"]["status"] == "pass", ( + stale_clean_lock_checks["benchmark-release-lock-self-consistency"] + ) + assert stale_clean_lock_checks["benchmark-clean-worktree-release-lock"]["status"] == "fail", ( + stale_clean_lock_checks["benchmark-clean-worktree-release-lock"] + ) + + generated_only_lock_root = TMP / "generated-only-lock-skill" + copy_reports(generated_only_lock_root) + generated_benchmark_path = generated_only_lock_root / "reports" / "benchmark_reproducibility.json" + generated_benchmark = json.loads(generated_benchmark_path.read_text(encoding="utf-8")) + generated_benchmark["git_status"]["dirty"] = True + generated_benchmark["git_status"]["changed_file_count"] = 4 + generated_benchmark["git_status"]["source_dirty"] = False + generated_benchmark["git_status"]["source_changed_file_count"] = 0 + generated_benchmark["git_status"]["generated_dirty"] = True + generated_benchmark["git_status"]["generated_changed_file_count"] = 4 + generated_benchmark["summary"]["release_lock_ready"] = True + generated_benchmark["summary"]["working_tree_dirty"] = True + generated_benchmark["summary"]["changed_file_count"] = 4 + generated_benchmark["summary"]["source_tree_dirty"] = False + generated_benchmark["summary"]["source_changed_file_count"] = 0 + generated_benchmark["summary"]["generated_tree_dirty"] = True + generated_benchmark["summary"]["generated_changed_file_count"] = 4 + generated_benchmark["release_lock"]["ready"] = True + generated_benchmark["release_lock"]["reason"] = "only generated evidence artifacts were dirty at generation time" + generated_benchmark_path.write_text( + json.dumps(generated_benchmark, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + generated_only_proc = run( + [ + sys.executable, + str(SCRIPT), + str(generated_only_lock_root), + "--output-json", + str(TMP / "generated_only_lock.json"), + "--output-md", + str(TMP / "generated_only_lock.md"), + "--generated-at", + "2026-06-15", + ] + ) + generated_only_payload = json.loads(generated_only_proc.stdout) + generated_only_checks = {item["key"]: item for item in generated_only_payload["checks"]} + assert generated_only_checks["benchmark-release-lock-self-consistency"]["status"] == "pass", ( + generated_only_checks["benchmark-release-lock-self-consistency"] + ) + + workflow_drift_root = TMP / "workflow-drift-skill" + copy_reports(workflow_drift_root) + studio_path = workflow_drift_root / "reports" / "review-studio.json" + studio = json.loads(studio_path.read_text(encoding="utf-8")) + for action in studio["review_actions"]: + if action.get("gate_key") == "world-class-evidence": + action["evidence_steps"] = [ + item for item in action["evidence_steps"] if item.get("key") != "native-client-telemetry" + ] + studio_path.write_text(json.dumps(studio, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + operator_runbook_path = workflow_drift_root / "reports" / "world_class_operator_runbook.json" + operator_runbook = json.loads(operator_runbook_path.read_text(encoding="utf-8")) + operator_runbook["coordination_plan"] = [ + item + for item in operator_runbook["coordination_plan"] + if item.get("evidence_key") != "native-client-telemetry" + ] + operator_runbook["summary"]["coordination_counts_as_completion"] = True + operator_runbook["release_gate"]["counts_as_completion"] = True + operator_runbook_path.write_text(json.dumps(operator_runbook, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + workflow_drift_proc = run( + [ + sys.executable, + str(SCRIPT), + str(workflow_drift_root), + "--output-json", + str(TMP / "workflow_drift.json"), + "--output-md", + str(TMP / "workflow_drift.md"), + "--generated-at", + "2026-06-15", + ] + ) + assert workflow_drift_proc.returncode == 2, workflow_drift_proc.stdout + workflow_drift_payload = json.loads(workflow_drift_proc.stdout) + workflow_drift_checks = {item["key"]: item for item in workflow_drift_payload["checks"]} + assert workflow_drift_checks["world-class-evidence-workflow-coverage"]["status"] == "fail", ( + workflow_drift_checks["world-class-evidence-workflow-coverage"] + ) + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_failure_regressions.py b/tests/verify_failure_regressions.py new file mode 100644 index 0000000..71080a9 --- /dev/null +++ b/tests/verify_failure_regressions.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +FAILURES_DIR = ROOT / "failures" +TRIGGER_EVAL = ROOT / "scripts" / "trigger_eval.py" +DESCRIPTION = ROOT / "evals" / "improved_description.txt" +BASELINE = ROOT / "evals" / "baseline_description.txt" +SEMANTIC_CONFIG = ROOT / "evals" / "semantic_config.json" + + +def run_case(case_file: Path) -> dict: + proc = subprocess.run( + [ + sys.executable, + str(TRIGGER_EVAL), + "--description-file", + str(DESCRIPTION), + "--baseline-description-file", + str(BASELINE), + "--cases", + str(case_file), + "--semantic-config", + str(SEMANTIC_CONFIG), + ], + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + payload["returncode"] = proc.returncode + payload["case_file"] = str(case_file.relative_to(ROOT)) + return payload + + +def main() -> None: + case_files = sorted(FAILURES_DIR.glob("*/cases.json")) + reports = [run_case(path) for path in case_files] + failures = [] + for report in reports: + if report["returncode"] != 0: + failures.append( + { + "case_file": report["case_file"], + "false_positives": report["false_positives"], + "false_negatives": report["false_negatives"], + "misfires": report["misfires"], + } + ) + + output = { + "ok": not failures, + "case_count": len(reports), + "families": [report["case_file"] for report in reports], + "failures": failures, + } + print(json.dumps(output, ensure_ascii=False, indent=2)) + if failures: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_feedback.py b/tests/verify_feedback.py new file mode 100644 index 0000000..bc43469 --- /dev/null +++ b/tests/verify_feedback.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_feedback" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "feedback-demo", + "--description", + "Turn repeated checklists into a reusable skill package.", + "--output-dir", + str(tmp_root), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = tmp_root / "feedback-demo" + proc = subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "collect_feedback.py"), + str(skill_dir), + "--note", + "This package needs clearer exclusions before it grows.", + "--rating", + "4", + "--category", + "boundary", + "--recommended-action", + "tighten-trigger", + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + assert payload["summary"]["count"] == 1, payload + feedback_json = Path(payload["artifacts"]["json"]) + assert feedback_json.exists(), feedback_json + stored = json.loads(feedback_json.read_text(encoding="utf-8")) + assert stored["entries"][0]["category"] == "boundary", stored + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_github_benchmark_scan.py b/tests/verify_github_benchmark_scan.py new file mode 100644 index 0000000..98245cd --- /dev/null +++ b/tests/verify_github_benchmark_scan.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "github_benchmark_scan.py" +FIXTURE_DIR = ROOT / "tests" / "fixtures" / "github_benchmark_scan" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_github_benchmark_scan" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + skill_dir = tmp_root / "benchmark-scan-demo" + (skill_dir / "reports").mkdir(parents=True, exist_ok=True) + (skill_dir / "SKILL.md").write_text( + "---\nname: benchmark-scan-demo\ndescription: Turn rough release notes into a reusable governed skill.\n---\n\n# Benchmark Scan Demo\n", + encoding="utf-8", + ) + + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(skill_dir), + "--query", + "release workflow evaluation portability", + "--fixture-dir", + str(FIXTURE_DIR), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["ok"], payload + assert payload["source"] == "fixture", payload + assert len(payload["repositories"]) == 3, payload + assert len(payload["external_references"]) == 3, payload + assert payload["borrow_prompt"], payload + + markdown = Path(payload["artifacts"]["markdown"]).read_text(encoding="utf-8") + assert "## Top 3 Benchmark Repositories" in markdown, markdown[:400] + assert "anthropics/skills" in markdown, markdown[:800] + assert "Cross-Repo Borrow Recommendations" in markdown, markdown[:1400] + + empty_fixture = tmp_root / "empty_fixture" + empty_fixture.mkdir(parents=True, exist_ok=True) + (empty_fixture / "bundle.json").write_text( + json.dumps({"search_items": [], "readmes": {}}, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + empty_skill_dir = tmp_root / "benchmark-empty-demo" + (empty_skill_dir / "reports").mkdir(parents=True, exist_ok=True) + (empty_skill_dir / "SKILL.md").write_text( + "---\nname: benchmark-empty-demo\ndescription: Turn a vague idea into a reusable skill.\n---\n\n# Empty Benchmark Demo\n", + encoding="utf-8", + ) + empty_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(empty_skill_dir), + "--query", + "unlikely benchmark query", + "--fixture-dir", + str(empty_fixture), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + empty_payload = json.loads(empty_proc.stdout) + assert empty_payload["ok"], empty_payload + assert empty_payload["repositories"] == [], empty_payload + assert empty_payload["cross_repo"]["borrow"] == [], empty_payload + assert empty_payload["external_references"] == [], empty_payload + assert empty_payload["borrow_prompt"].startswith("No benchmark suggestions"), empty_payload + empty_markdown = Path(empty_payload["artifacts"]["markdown"]).read_text(encoding="utf-8") + assert "No benchmark repositories were collected." in empty_markdown, empty_markdown[:600] + assert "Review the three benchmark objects" not in empty_markdown, empty_markdown[:600] + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_html_rendering.py b/tests/verify_html_rendering.py new file mode 100644 index 0000000..32fb436 --- /dev/null +++ b/tests/verify_html_rendering.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import json +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "scripts")) + +from html_rendering import html_text # noqa: E402 + + +def main() -> None: + assert html_text(None) == "" + assert html_text(0) == "0" + assert html_text(False) == "False" + assert html_text("") == "" + assert html_text("&") == "<tag data-x="1">&</tag>" + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_install_simulation.py b/tests/verify_install_simulation.py new file mode 100644 index 0000000..5c3b9e3 --- /dev/null +++ b/tests/verify_install_simulation.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +import tempfile +import zipfile +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_install_simulation" +PACKAGER = ROOT / "scripts" / "cross_packager.py" +SIMULATOR = ROOT / "scripts" / "simulate_install.py" +EXPECTATIONS = ROOT / "evals" / "packaging_expectations.json" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = {} + if proc.stdout.strip(): + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def build_package(out_dir: Path, skill_root: Path = ROOT) -> dict: + return run( + [ + sys.executable, + str(PACKAGER), + str(skill_root), + "--platform", + "openai", + "--platform", + "claude", + "--platform", + "generic", + "--platform", + "vscode", + "--expectations", + str(EXPECTATIONS), + "--output-dir", + str(out_dir), + "--zip", + ] + ) + + +def simulate(package_dir: Path, output_json: Path, output_md: Path, skill_root: Path = ROOT) -> dict: + return run( + [ + sys.executable, + str(SIMULATOR), + str(skill_root), + "--package-dir", + str(package_dir), + "--install-root", + str(TMP / "install-root"), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-13", + ] + ) + + +def rewrite_archive_json(package_dir: Path, relative_path: str, transform) -> None: + archive_path = package_dir / "yao-meta-skill.zip" + rewritten_path = package_dir / "yao-meta-skill.rewritten.zip" + archive_member = f"yao-meta-skill/{relative_path}" + replaced = False + with zipfile.ZipFile(archive_path) as archive_in, zipfile.ZipFile(rewritten_path, "w", compression=zipfile.ZIP_DEFLATED) as archive_out: + for info in archive_in.infolist(): + data = archive_in.read(info.filename) + if info.filename == archive_member: + payload = json.loads(data.decode("utf-8")) + data = (json.dumps(transform(payload), ensure_ascii=False, indent=2) + "\n").encode("utf-8") + replaced = True + archive_out.writestr(info, data) + assert replaced, archive_member + rewritten_path.replace(archive_path) + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + + valid_dir = TMP / "dist" + build = build_package(valid_dir) + assert build["ok"], build + valid = simulate(valid_dir, TMP / "install_simulation.json", TMP / "install_simulation.md") + payload = valid["payload"] + assert valid["ok"], valid + assert payload["ok"], payload + assert payload["summary"]["archive_extracted"], payload + assert payload["summary"]["entrypoint_loaded"], payload + assert payload["summary"]["manifest_loaded"], payload + assert payload["summary"]["interface_loaded"], payload + assert payload["summary"]["nested_skill_entry_count"] == 0, payload + assert payload["summary"]["adapter_count"] == 4, payload + assert payload["summary"]["installer_permission_enforced_count"] == 12, payload + assert payload["summary"]["installer_permission_failure_count"] == 0, payload + assert payload["summary"]["permission_target_count"] == 4, payload + assert payload["summary"]["permission_capability_count"] == 3, payload + assert not payload["failures"], payload + valid_markdown = (TMP / "install_simulation.md").read_text(encoding="utf-8") + assert "Install Simulation" in valid_markdown + assert "Nested SKILL.md entries" in valid_markdown + assert "Installer permissions enforced" in valid_markdown + + with tempfile.TemporaryDirectory(prefix="renamed-install-root-") as temp_root: + renamed_root = Path(temp_root) / "checkout-alias" + shutil.copytree( + ROOT, + renamed_root, + ignore=shutil.ignore_patterns(".git", ".previews", "dist", "__pycache__", ".pytest_cache", "tmp*"), + ) + renamed_dir = TMP / "renamed-dist" + renamed_build = build_package(renamed_dir, renamed_root) + assert renamed_build["ok"], renamed_build + assert (renamed_dir / "yao-meta-skill.zip").exists(), renamed_build + renamed_valid = simulate(renamed_dir, TMP / "renamed_install_simulation.json", TMP / "renamed_install_simulation.md", renamed_root) + assert renamed_valid["ok"], renamed_valid + assert renamed_valid["payload"]["summary"]["archive_extracted"], renamed_valid + assert renamed_valid["payload"]["installed_skill_dir"].endswith("simulate-yao-meta-skill/yao-meta-skill"), renamed_valid + + policy_gap_dir = TMP / "policy-gap-dist" + shutil.copytree(valid_dir, policy_gap_dir) + + def remove_vscode_network_enforcement(payload: dict) -> dict: + payload["capabilities"]["network"]["target_enforcement"].pop("vscode", None) + return payload + + rewrite_archive_json(policy_gap_dir, "security/permission_policy.json", remove_vscode_network_enforcement) + policy_gap = simulate(policy_gap_dir, TMP / "policy_gap.json", TMP / "policy_gap.md") + policy_gap_payload = policy_gap["payload"] + assert policy_gap["returncode"] == 2, policy_gap + assert not policy_gap_payload["ok"], policy_gap_payload + assert policy_gap_payload["summary"]["installer_permission_enforced_count"] == 11, policy_gap_payload + assert policy_gap_payload["summary"]["installer_permission_failure_count"] >= 1, policy_gap_payload + assert any("vscode capability network has target enforcement note" in item for item in policy_gap_payload["failures"]), policy_gap_payload + + nested_skill_dir = TMP / "nested-skill-dist" + shutil.copytree(valid_dir, nested_skill_dir) + with zipfile.ZipFile(nested_skill_dir / "yao-meta-skill.zip", "a", compression=zipfile.ZIP_DEFLATED) as archive: + archive.writestr("yao-meta-skill/tests/fixtures/broken/SKILL.md", "---\nname: broken\n---\n") + nested_skill = simulate(nested_skill_dir, TMP / "nested_skill.json", TMP / "nested_skill.md") + nested_skill_payload = nested_skill["payload"] + assert nested_skill["returncode"] == 2, nested_skill + assert not nested_skill_payload["ok"], nested_skill_payload + assert nested_skill_payload["summary"]["nested_skill_entry_count"] == 1, nested_skill_payload + assert any("Installed package exposes only the root SKILL.md entrypoint" in item for item in nested_skill_payload["failures"]), nested_skill_payload + + unsafe_dir = TMP / "unsafe-dist" + shutil.copytree(valid_dir, unsafe_dir) + with zipfile.ZipFile(unsafe_dir / "yao-meta-skill.zip", "a", compression=zipfile.ZIP_DEFLATED) as archive: + archive.writestr("../evil.txt", "bad") + unsafe = simulate(unsafe_dir, TMP / "unsafe.json", TMP / "unsafe.md") + unsafe_payload = unsafe["payload"] + assert unsafe["returncode"] == 2, unsafe + assert not unsafe_payload["ok"], unsafe_payload + assert any("Archive has no absolute or parent-traversal entries" in item for item in unsafe_payload["failures"]), unsafe_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_intent_confidence.py b/tests/verify_intent_confidence.py new file mode 100644 index 0000000..ef3c07d --- /dev/null +++ b/tests/verify_intent_confidence.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_intent_confidence.py" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_intent_confidence" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + skill_dir = tmp_root / "intent-confidence-demo" + (skill_dir / "reports").mkdir(parents=True, exist_ok=True) + (skill_dir / "SKILL.md").write_text( + "---\nname: intent-confidence-demo\ndescription: Turn repeated incident notes into a reusable incident packet.\n---\n\n# Intent Confidence Demo\n", + encoding="utf-8", + ) + context_path = skill_dir / "reports" / "context.json" + context_path.write_text( + json.dumps( + { + "job": "Turn repeated incident notes into a reusable incident packet.", + "real_inputs": ["incident notes", "chat timeline"], + "primary_output": "A reusable incident command packet.", + "description": "Turn repeated incident notes into a reusable incident packet. Primary output: A reusable incident command packet.", + "exclusions": ["Do not draft external PR statements."], + "constraints": ["auditability", "portability"], + "standards": ["consistency"], + "correction": "", + "user_references": ["A trusted incident workflow"], + }, + ensure_ascii=False, + indent=2, + ), + encoding="utf-8", + ) + + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(skill_dir), + "--context-json", + str(context_path), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["summary"]["gate_passed"], payload + assert payload["summary"]["score"] >= 70, payload + markdown = Path(payload["artifacts"]["markdown"]).read_text(encoding="utf-8") + assert "Intent Confidence" in markdown, markdown[:200] + assert "Follow-Up Questions" in markdown, markdown[:500] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_iteration_directions.py b/tests/verify_iteration_directions.py new file mode 100644 index 0000000..9645e28 --- /dev/null +++ b/tests/verify_iteration_directions.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" + + +def run(*args: str) -> dict: + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + return json.loads(proc.stdout) + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_iteration_directions" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + init_payload = run( + "init", + "iteration-demo-skill", + "--description", + "Turn a repeated release checklist into a reusable reusable skill package.", + "--output-dir", + str(tmp_root), + ) + created = Path(init_payload["root"]) + report = run("iteration-directions", str(created)) + assert report["summary"]["selection_rule"], report + assert report["summary"]["recommended_now"], report + assert report["summary"]["defer_for_now"], report + assert len(report["directions"]) == 3, report + titles = {item["title"] for item in report["directions"]} + assert "Tighten trigger and exclusions" in titles or "Add the first execution asset" in titles, titles + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_local_install_sync.py b/tests/verify_local_install_sync.py new file mode 100644 index 0000000..d9b9d73 --- /dev/null +++ b/tests/verify_local_install_sync.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +import zipfile +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SYNC_SCRIPT = ROOT / "scripts" / "sync_local_install.py" +TMP = ROOT / "tests" / "tmp" / "local_install_sync" +PACKAGER = ROOT / "scripts" / "cross_packager.py" +EXPECTATIONS = ROOT / "evals" / "packaging_expectations.json" + + +def build_package(out_dir: Path) -> dict: + proc = subprocess.run( + [ + sys.executable, + str(PACKAGER), + str(ROOT), + "--platform", + "openai", + "--platform", + "claude", + "--platform", + "generic", + "--platform", + "vscode", + "--expectations", + str(EXPECTATIONS), + "--output-dir", + str(out_dir), + "--zip", + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return {"ok": proc.returncode == 0, "payload": payload, "stderr": proc.stderr} + + +def rewrite_archive_json(package_dir: Path, relative_path: str, transform) -> None: + archive_path = package_dir / "yao-meta-skill.zip" + rewritten_path = package_dir / "yao-meta-skill.rewritten.zip" + archive_member = f"yao-meta-skill/{relative_path}" + replaced = False + with zipfile.ZipFile(archive_path) as archive_in, zipfile.ZipFile(rewritten_path, "w", compression=zipfile.ZIP_DEFLATED) as archive_out: + for info in archive_in.infolist(): + data = archive_in.read(info.filename) + if info.filename == archive_member: + payload = json.loads(data.decode("utf-8")) + data = (json.dumps(transform(payload), ensure_ascii=False, indent=2) + "\n").encode("utf-8") + replaced = True + archive_out.writestr(info, data) + assert replaced, archive_member + rewritten_path.replace(archive_path) + + +def run_sync(install_dir: Path, package_dir: Path) -> dict: + proc = subprocess.run( + [ + sys.executable, + str(SYNC_SCRIPT), + "--install-dir", + str(install_dir), + "--package-dir", + str(package_dir), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def run_sync_raw(install_dir: Path, package_dir: Path) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(SYNC_SCRIPT), + "--install-dir", + str(install_dir), + "--package-dir", + str(package_dir), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + package_dir = TMP / "dist" + package = build_package(package_dir) + assert package["ok"], package + + install_dir = TMP / "installed-skill" + install_dir.mkdir(parents=True) + (install_dir / "SKILL.md").write_text( + "---\nname: yao-meta-skill\ndescription: local install fixture\n---\n", + encoding="utf-8", + ) + stale_file = install_dir / "stale.txt" + stale_file.write_text("old install artifact\n", encoding="utf-8") + git_config = install_dir / ".git" / "config" + git_config.parent.mkdir(parents=True) + git_config.write_text("[core]\n\trepositoryformatversion = 0\n", encoding="utf-8") + + untracked_file = ROOT / "sync-local-untracked.tmp" + untracked_file.write_text("do not copy me\n", encoding="utf-8") + try: + result = run_sync(install_dir, package_dir) + finally: + untracked_file.unlink(missing_ok=True) + + policy_gap_dir = TMP / "policy-gap-dist" + shutil.copytree(package_dir, policy_gap_dir) + + def remove_vscode_network_enforcement(payload: dict) -> dict: + payload["capabilities"]["network"]["target_enforcement"].pop("vscode", None) + return payload + + rewrite_archive_json(policy_gap_dir, "security/permission_policy.json", remove_vscode_network_enforcement) + refused_install_dir = TMP / "preflight-refused-install" + refused_install_dir.mkdir(parents=True) + (refused_install_dir / "SKILL.md").write_text( + "---\nname: yao-meta-skill\ndescription: local install fixture\n---\n", + encoding="utf-8", + ) + refused_stale = refused_install_dir / "stale.txt" + refused_stale.write_text("must not be touched after preflight failure\n", encoding="utf-8") + preflight_refused = run_sync_raw(refused_install_dir, policy_gap_dir) + + ordinary_dir = TMP / "ordinary-folder" + ordinary_dir.mkdir(parents=True) + ordinary_file = ordinary_dir / "keep.txt" + ordinary_file.write_text("must survive a refused sync\n", encoding="utf-8") + refused = run_sync_raw(ordinary_dir, package_dir) + + makefile_text = (ROOT / "Makefile").read_text(encoding="utf-8") + readme_text = (ROOT / "README.md").read_text(encoding="utf-8") + + checks = { + "sync_ok": result["ok"], + "skill_md_copied": (install_dir / "SKILL.md").exists(), + "script_copied": (install_dir / "scripts" / "yao.py").exists(), + "untracked_file_skipped": not (install_dir / "sync-local-untracked.tmp").exists(), + "untracked_business_skill_skipped": not (install_dir / "geo-ranking-article-generator").exists(), + "stale_file_removed": not stale_file.exists(), + "install_git_metadata_preserved": git_config.exists(), + "install_sentinel_written": (install_dir / ".yao-local-install.json").exists(), + "install_preflight_enforced": result["payload"]["install_preflight"]["installer_permission_enforced_count"] == 12, + "install_preflight_permission_failures_zero": result["payload"]["install_preflight"]["installer_permission_failure_count"] == 0, + "install_preflight_blocks_sync": preflight_refused.returncode != 0, + "install_preflight_failure_preserves_files": refused_stale.exists(), + "ordinary_dir_refused": refused.returncode != 0, + "ordinary_dir_preserved": ordinary_file.exists(), + "makefile_target_present": "sync-local-install" in makefile_text, + "makefile_defaults_disabled": "LOCAL_SKILL_INSTALL_DIR ?= $(HOME)/.agents/skills.disabled/yao-meta-skill" + in makefile_text, + "makefile_active_opt_in_present": "sync-active-install" in makefile_text, + "readme_names_development_source": "Development source" in readme_text, + "readme_names_disabled_mirror": "~/.agents/skills.disabled/yao-meta-skill" in readme_text, + } + report = { + "ok": all(checks.values()), + "checks": checks, + "sync_result": result, + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_operator_ux.py b/tests/verify_operator_ux.py new file mode 100644 index 0000000..4f81ddb --- /dev/null +++ b/tests/verify_operator_ux.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +import json +import os +import shutil +import stat +import textwrap +from pathlib import Path + +from yao_cli_helpers import ROOT, run, run_with_env + + +def write_skill(path: Path, description: str = "Operator UX test skill.") -> None: + path.mkdir(parents=True, exist_ok=True) + (path / "SKILL.md").write_text( + f"---\nname: yao-meta-skill\ndescription: {description}\n---\n\n# Test Skill\n", + encoding="utf-8", + ) + + +def write_fake_gh(bin_dir: Path) -> None: + bin_dir.mkdir(parents=True, exist_ok=True) + script = bin_dir / "gh" + script.write_text( + textwrap.dedent( + """\ + #!/usr/bin/env python3 + import json + import os + import sys + + args = sys.argv[1:] + if args[:2] == ["pr", "view"]: + checks = [] if os.environ.get("YAO_FAKE_GH_EMPTY_CHECKS") else [ + { + "__typename": "CheckRun", + "name": "test", + "status": "COMPLETED", + "conclusion": "SUCCESS", + "workflowName": "test", + } + ] + print(json.dumps({ + "number": 4, + "title": "docs: clarify Python 3.11 local development", + "state": "OPEN", + "isDraft": False, + "mergeable": "MERGEABLE", + "author": {"login": "contributor"}, + "baseRefName": "main", + "headRefName": "docs-python-311", + "url": "https://github.com/example/repo/pull/4", + "additions": 23, + "deletions": 3, + "changedFiles": 2, + "commits": [{"oid": "abc123", "messageHeadline": "docs: clarify Python 3.11 local development"}], + "statusCheckRollup": checks, + "reviewDecision": "", + "maintainerCanModify": True, + })) + raise SystemExit(0) + if args[:2] == ["pr", "diff"]: + print("Makefile") + print("README.md") + raise SystemExit(0) + print("unexpected gh invocation", args, file=sys.stderr) + raise SystemExit(2) + """ + ), + encoding="utf-8", + ) + script.chmod(script.stat().st_mode | stat.S_IXUSR) + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_operator_ux" + shutil.rmtree(tmp_root, ignore_errors=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + source = tmp_root / "source" / "yao-meta-skill" + codex_root = tmp_root / "codex-skills" + agents_root = tmp_root / "agents-skills" + disabled_root = tmp_root / "disabled-skills" + codex_root.mkdir() + agents_root.mkdir() + disabled_root.mkdir() + write_skill(source) + (codex_root / "yao-meta-skill").symlink_to(source, target_is_directory=True) + write_skill(disabled_root / "yao-meta-skill", "Disabled mirror.") + + install_status = run( + "install-status", + "--expected-source", + str(source), + "--codex-root", + str(codex_root), + "--agents-root", + str(agents_root), + "--disabled-root", + str(disabled_root), + ) + assert install_status["ok"], install_status + assert install_status["payload"]["summary"]["codex_active"] is True, install_status + assert install_status["payload"]["summary"]["agents_active"] is False, install_status + assert install_status["payload"]["summary"]["disabled_mirror"] is True, install_status + assert install_status["payload"]["locations"]["codex"]["is_symlink"] is True, install_status + assert install_status["payload"]["locations"]["codex"]["points_to_expected_source"] is True, install_status + assert any("restart" in item.lower() for item in install_status["payload"]["recommendations"]), install_status + + sync_ok = run("localized-doc-sync-check") + assert sync_ok["ok"], sync_ok + assert sync_ok["payload"]["summary"]["missing_count"] == 0, sync_ok + assert any(item["key"] == "operator-ux" for item in sync_ok["payload"]["pairs"]), sync_ok + + source_readme = tmp_root / "README.md" + localized_readme = tmp_root / "README.zh-CN.md" + source_readme.write_text("## Skill OS 2.0 Upgrade\n## 2.0 Use Cases\n", encoding="utf-8") + localized_readme.write_text("## Skill OS 2.0 升级\n", encoding="utf-8") + sync_fail = run( + "localized-doc-sync-check", + "--source", + str(source_readme), + "--localized", + str(localized_readme), + "--pair", + "upgrade::## Skill OS 2.0 Upgrade::## Skill OS 2.0 升级", + "--pair", + "use-cases::## 2.0 Use Cases::## 2.0 使用场景", + "--output-json", + str(tmp_root / "sync.json"), + "--output-md", + str(tmp_root / "sync.md"), + ) + assert not sync_fail["ok"], sync_fail + assert sync_fail["returncode"] == 2, sync_fail + assert sync_fail["payload"]["summary"]["missing_count"] == 1, sync_fail + assert (tmp_root / "sync.json").exists(), sync_fail + assert "use-cases" in (tmp_root / "sync.md").read_text(encoding="utf-8"), sync_fail + + sync_missing_file = run( + "localized-doc-sync-check", + "--source", + str(tmp_root / "missing-README.md"), + "--localized", + str(localized_readme), + ) + assert not sync_missing_file["ok"], sync_missing_file + assert sync_missing_file["payload"]["failures"], sync_missing_file + assert "Missing docs file" in sync_missing_file["payload"]["failures"][0], sync_missing_file + + fake_bin = tmp_root / "bin" + write_fake_gh(fake_bin) + fake_env = {"PATH": f"{fake_bin}{os.pathsep}{os.environ.get('PATH', '')}"} + pr_report = run_with_env( + fake_env, + "pr-review-report", + "4", + "--repo", + "example/repo", + "--output-json", + str(tmp_root / "pr.json"), + "--output-md", + str(tmp_root / "pr.md"), + ) + assert pr_report["ok"], pr_report + assert pr_report["payload"]["decision"] == "mergeable-after-review", pr_report + assert pr_report["payload"]["review_depth"] == "quick", pr_report + assert pr_report["payload"]["checks"]["passed_count"] == 1, pr_report + assert pr_report["payload"]["files"] == ["Makefile", "README.md"], pr_report + assert (tmp_root / "pr.json").exists(), pr_report + assert "Suggested Commands" in (tmp_root / "pr.md").read_text(encoding="utf-8"), pr_report + + empty_check_env = dict(fake_env) + empty_check_env["YAO_FAKE_GH_EMPTY_CHECKS"] = "1" + pr_without_checks = run_with_env( + empty_check_env, + "pr-review-report", + "4", + "--repo", + "example/repo", + ) + assert pr_without_checks["ok"], pr_without_checks + assert pr_without_checks["payload"]["decision"] == "local-verification-required", pr_without_checks + pr_require_checks = run_with_env( + empty_check_env, + "pr-review-report", + "4", + "--repo", + "example/repo", + "--require-checks", + ) + assert not pr_require_checks["ok"], pr_require_checks + assert pr_require_checks["payload"]["decision"] == "checks-required", pr_require_checks + + print("operator ux checks passed") + + +if __name__ == "__main__": + main() diff --git a/tests/verify_output_eval_lab.py b/tests/verify_output_eval_lab.py new file mode 100644 index 0000000..bf7ebf5 --- /dev/null +++ b/tests/verify_output_eval_lab.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "run_output_eval.py" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_output_eval" + tmp_root.mkdir(parents=True, exist_ok=True) + output_json = tmp_root / "output_quality_scorecard.json" + output_md = tmp_root / "output_quality_scorecard.md" + blind_pack_json = tmp_root / "output_blind_review_pack.json" + blind_pack_md = tmp_root / "output_blind_review_pack.md" + blind_answer_key_json = tmp_root / "output_blind_answer_key.json" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--blind-pack-json", + str(blind_pack_json), + "--blind-pack-md", + str(blind_pack_md), + "--blind-answer-key-json", + str(blind_answer_key_json), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["ok"], payload + assert payload["summary"]["case_count"] == 5, payload + assert payload["summary"]["file_backed_case_count"] == 1, payload + assert payload["summary"]["near_neighbor_case_count"] == 1, payload + assert payload["summary"]["boundary_case_count"] == 1, payload + assert payload["summary"]["with_skill_pass_rate"] > payload["summary"]["baseline_pass_rate"], payload + assert payload["summary"]["delta"] > 0, payload + assert payload["summary"]["gate_pass"], payload + assert payload["summary"]["blind_pair_count"] == 5, payload + assert payload["blind_review"]["pair_count"] == 5, payload + assert output_json.exists(), output_json + assert output_md.exists(), output_md + assert blind_pack_json.exists(), blind_pack_json + assert blind_pack_md.exists(), blind_pack_md + assert blind_answer_key_json.exists(), blind_answer_key_json + blind_pack = json.loads(blind_pack_json.read_text(encoding="utf-8")) + answer_key = json.loads(blind_answer_key_json.read_text(encoding="utf-8")) + assert blind_pack["summary"]["pair_count"] == 5, blind_pack + assert blind_pack["summary"]["answer_key_separate"], blind_pack + assert answer_key["summary"]["pair_count"] == 5, answer_key + assert "variant_a_role" not in json.dumps(blind_pack, ensure_ascii=False), blind_pack + assert "variant_b_role" not in json.dumps(blind_pack, ensure_ascii=False), blind_pack + assert "variant_a_role" in json.dumps(answer_key, ensure_ascii=False), answer_key + assert all(pair["review_instruction"].startswith("Pick A or B") for pair in blind_pack["pairs"]), blind_pack + markdown = output_md.read_text(encoding="utf-8") + assert "Output Quality Scorecard" in markdown, markdown[:400] + assert "with-skill" in markdown, markdown[:600] + assert "Blind A/B pairs" in markdown, markdown[:800] + assert "Failure Taxonomy" in markdown, markdown[:1200] + blind_markdown = blind_pack_md.read_text(encoding="utf-8") + assert "Output Blind A/B Review Pack" in blind_markdown, blind_markdown[:400] + assert "Answer key separate" in blind_markdown, blind_markdown[:600] + + invalid_cases = tmp_root / "invalid_cases.jsonl" + invalid_cases.write_text(json.dumps({"id": "bad", "prompt": "x"}) + "\n", encoding="utf-8") + invalid_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(invalid_cases), + "--output-json", + str(tmp_root / "invalid.json"), + "--output-md", + str(tmp_root / "invalid.md"), + "--blind-pack-json", + str(tmp_root / "invalid_blind.json"), + "--blind-pack-md", + str(tmp_root / "invalid_blind.md"), + "--blind-answer-key-json", + str(tmp_root / "invalid_answer_key.json"), + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + assert invalid_proc.returncode == 2, invalid_proc.stdout + invalid_payload = json.loads(invalid_proc.stdout) + assert not invalid_payload["ok"], invalid_payload + assert invalid_payload["failures"], invalid_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_output_execution_runs.py b/tests/verify_output_execution_runs.py new file mode 100644 index 0000000..371700c --- /dev/null +++ b/tests/verify_output_execution_runs.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +import json +import os +import shutil +import subprocess +import sys +import threading +from http.server import BaseHTTPRequestHandler, HTTPServer +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "run_output_execution.py" +LOCAL_RUNNER = ROOT / "scripts" / "local_output_eval_runner.py" +PROVIDER_RUNNER = ROOT / "scripts" / "provider_output_eval_runner.py" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_output_execution" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + + recorded_json = tmp_root / "recorded.json" + recorded_md = tmp_root / "recorded.md" + recorded_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(recorded_json), + "--output-md", + str(recorded_md), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + recorded = json.loads(recorded_proc.stdout) + assert recorded["ok"], recorded + assert recorded["summary"]["case_count"] == 5, recorded + assert recorded["summary"]["variant_run_count"] == 10, recorded + assert recorded["summary"]["recorded_fixture_count"] == 10, recorded + assert recorded["summary"]["command_executed_count"] == 0, recorded + assert recorded["summary"]["model_executed_count"] == 0, recorded + assert recorded["summary"]["token_estimated_count"] == 10, recorded + assert recorded["summary"]["with_skill_pass_rate"] > recorded["summary"]["baseline_pass_rate"], recorded + assert "No model-executed runs are recorded yet" in recorded_md.read_text(encoding="utf-8"), recorded_md + + local_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(tmp_root / "local.json"), + "--output-md", + str(tmp_root / "local.md"), + "--runner-command", + json.dumps([sys.executable, str(LOCAL_RUNNER)]), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + local = json.loads(local_proc.stdout) + assert local["ok"], local + assert local["summary"]["variant_run_count"] == 10, local + assert local["summary"]["command_executed_count"] == 10, local + assert local["summary"]["recorded_fixture_count"] == 0, local + assert local["summary"]["model_executed_count"] == 0, local + assert local["summary"]["timing_observed_count"] == 10, local + assert local["summary"]["token_estimated_count"] == 10, local + assert all(item["provider"] == "local-output-eval-runner" for item in local["runs"]), local + local_md = (tmp_root / "local.md").read_text(encoding="utf-8") + assert "Command runner evidence is present" in local_md, local_md + assert "not provider-backed model evidence" in local_md, local_md + + runner = tmp_root / "runner.py" + runner.write_text( + "\n".join( + [ + "import json, sys, time", + "request = json.loads(sys.stdin.read())", + "time.sleep(0.001)", + "print(json.dumps({", + " 'output': request['fixture_output'],", + " 'execution_kind': 'model',", + " 'provider': 'local-fixture',", + " 'model': 'fixture-model',", + " 'usage': {'input_tokens': 11, 'output_tokens': 17, 'total_tokens': 28}", + "}))", + ] + ) + + "\n", + encoding="utf-8", + ) + command_json = json.dumps([sys.executable, str(runner)]) + command_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(tmp_root / "command.json"), + "--output-md", + str(tmp_root / "command.md"), + "--runner-command", + command_json, + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + command = json.loads(command_proc.stdout) + assert command["ok"], command + assert command["summary"]["command_executed_count"] == 10, command + assert command["summary"]["model_executed_count"] == 10, command + assert command["summary"]["timing_observed_count"] == 10, command + assert command["summary"]["token_observed_count"] == 10, command + assert command["summary"]["token_estimated_count"] == 0, command + assert all(item["duration_ms"] is not None for item in command["runs"]), command + assert all(item["model"] == "fixture-model" for item in command["runs"]), command + + metadata_only_runner = tmp_root / "metadata_only_runner.py" + metadata_only_runner.write_text( + "\n".join( + [ + "import json, sys", + "request = json.loads(sys.stdin.read())", + "print(json.dumps({", + " 'output': request['fixture_output'],", + " 'provider': 'metadata-only-provider',", + " 'model': 'metadata-only-model',", + " 'usage': {'input_tokens': 11, 'output_tokens': 17, 'total_tokens': 28}", + "}))", + ] + ) + + "\n", + encoding="utf-8", + ) + metadata_only_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(tmp_root / "metadata-only.json"), + "--output-md", + str(tmp_root / "metadata-only.md"), + "--runner-command", + json.dumps([sys.executable, str(metadata_only_runner)]), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + metadata_only = json.loads(metadata_only_proc.stdout) + assert metadata_only["ok"], metadata_only + assert metadata_only["summary"]["command_executed_count"] == 10, metadata_only + assert metadata_only["summary"]["model_executed_count"] == 0, metadata_only + assert metadata_only["summary"]["token_observed_count"] == 10, metadata_only + assert all(item["execution_mode"] == "command" for item in metadata_only["runs"]), metadata_only + + provider_request = { + "case_id": "provider-contract", + "variant": "with_skill", + "prompt": "Create a reusable skill package.", + "input_files": [], + "metadata": {}, + } + missing_env = os.environ.copy() + missing_env.pop("OPENAI_API_KEY", None) + missing_env.pop("YAO_OUTPUT_EVAL_MODEL", None) + missing_provider_proc = subprocess.run( + [sys.executable, str(PROVIDER_RUNNER), "--model", "fixture-model"], + cwd=ROOT, + input=json.dumps(provider_request), + capture_output=True, + text=True, + env=missing_env, + ) + assert missing_provider_proc.returncode == 2, missing_provider_proc.stdout + assert "missing API key env" in missing_provider_proc.stderr, missing_provider_proc.stderr + missing_deepseek_proc = subprocess.run( + [sys.executable, str(PROVIDER_RUNNER), "--provider", "deepseek", "--model", "fixture-model"], + cwd=ROOT, + input=json.dumps(provider_request), + capture_output=True, + text=True, + env=missing_env, + ) + assert missing_deepseek_proc.returncode == 2, missing_deepseek_proc.stdout + assert "missing API key env: DEEPSEEK_API_KEY" in missing_deepseek_proc.stderr, missing_deepseek_proc.stderr + + custom_env = os.environ.copy() + custom_env["OPENAI_API_KEY"] = "test-key" + custom_env["DEEPSEEK_API_KEY"] = "test-key" + custom_env["YAO_OUTPUT_EVAL_MODEL"] = "fixture-model" + custom_host_proc = subprocess.run( + [ + sys.executable, + str(PROVIDER_RUNNER), + "--base-url", + "https://example.com/v1/responses", + ], + cwd=ROOT, + input=json.dumps(provider_request), + capture_output=True, + text=True, + env=custom_env, + ) + assert custom_host_proc.returncode == 2, custom_host_proc.stdout + assert "custom provider host requires --allow-custom-base-url" in custom_host_proc.stderr, custom_host_proc.stderr + + wrong_path_proc = subprocess.run( + [ + sys.executable, + str(PROVIDER_RUNNER), + "--base-url", + "https://api.openai.com/v1/chat/completions", + ], + cwd=ROOT, + input=json.dumps(provider_request), + capture_output=True, + text=True, + env=custom_env, + ) + assert wrong_path_proc.returncode == 2, wrong_path_proc.stdout + assert "provider endpoint path must start with /v1/responses" in wrong_path_proc.stderr, wrong_path_proc.stderr + empty_path_proc = subprocess.run( + [ + sys.executable, + str(PROVIDER_RUNNER), + "--base-url", + "https://api.openai.com", + ], + cwd=ROOT, + input=json.dumps(provider_request), + capture_output=True, + text=True, + env=custom_env, + ) + assert empty_path_proc.returncode == 2, empty_path_proc.stdout + assert "provider endpoint path must start with /v1/responses" in empty_path_proc.stderr, empty_path_proc.stderr + + class ProviderHandler(BaseHTTPRequestHandler): + requests: list[dict] = [] + + def do_POST(self) -> None: # noqa: N802 + length = int(self.headers.get("Content-Length", "0")) + body = self.rfile.read(length).decode("utf-8") + ProviderHandler.requests.append( + { + "path": self.path, + "authorization": self.headers.get("Authorization", ""), + "body": json.loads(body), + } + ) + if self.path.endswith("/chat/completions"): + payload = { + "id": "chat_fixture", + "choices": [ + { + "message": { + "role": "assistant", + "content": "Create SKILL.md and reports/skill-overview.html for this reusable package.", + } + } + ], + "usage": {"prompt_tokens": 21, "completion_tokens": 9, "total_tokens": 30}, + } + else: + payload = { + "id": "resp_fixture", + "output": [ + { + "content": [ + { + "type": "output_text", + "text": "Create SKILL.md and reports/skill-overview.html for this reusable package.", + } + ] + } + ], + "usage": {"input_tokens": 21, "output_tokens": 9, "total_tokens": 30}, + } + encoded = json.dumps(payload).encode("utf-8") + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(encoded))) + self.end_headers() + self.wfile.write(encoded) + + def log_message(self, format: str, *args: object) -> None: + return + + server = HTTPServer(("127.0.0.1", 0), ProviderHandler) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + provider_url = f"http://127.0.0.1:{server.server_port}/v1/responses" + provider_env = os.environ.copy() + provider_env["OPENAI_API_KEY"] = "test-key" + provider_env["DEEPSEEK_API_KEY"] = "test-key" + provider_env["YAO_OUTPUT_EVAL_MODEL"] = "fixture-model" + try: + direct_provider_proc = subprocess.run( + [ + sys.executable, + str(PROVIDER_RUNNER), + "--base-url", + provider_url, + "--allow-insecure-localhost", + ], + cwd=ROOT, + input=json.dumps(provider_request), + capture_output=True, + text=True, + env=provider_env, + check=True, + ) + direct_provider = json.loads(direct_provider_proc.stdout) + assert direct_provider["execution_kind"] == "model", direct_provider + assert direct_provider["provider"] == "openai", direct_provider + assert direct_provider["model"] == "fixture-model", direct_provider + assert direct_provider["usage"]["estimated"] is False, direct_provider + assert ProviderHandler.requests[-1]["path"] == "/v1/responses", ProviderHandler.requests[-1] + assert ProviderHandler.requests[-1]["authorization"] == "Bearer test-key", ProviderHandler.requests[-1] + assert ProviderHandler.requests[-1]["body"]["model"] == "fixture-model", ProviderHandler.requests[-1] + assert ProviderHandler.requests[-1]["body"]["temperature"] == 0.0, ProviderHandler.requests[-1] + assert "Create a reusable skill package." in ProviderHandler.requests[-1]["body"]["input"], ProviderHandler.requests[-1] + assert "fixture_output" not in ProviderHandler.requests[-1]["body"]["input"], ProviderHandler.requests[-1] + + governed_provider_request = { + "case_id": "file-backed-governed-package", + "variant": "with_skill", + "prompt": "Turn the attached release brief source into a governed skill package.", + "input_files": ["fixtures/release-brief-source.md"], + "metadata": {"case_type": "boundary", "tier": "governed"}, + } + governed_provider_proc = subprocess.run( + [ + sys.executable, + str(PROVIDER_RUNNER), + "--base-url", + provider_url, + "--allow-insecure-localhost", + "--model", + "fixture-model", + ], + cwd=ROOT, + input=json.dumps(governed_provider_request), + capture_output=True, + text=True, + env=provider_env, + check=True, + ) + governed_provider = json.loads(governed_provider_proc.stdout) + assert governed_provider["execution_kind"] == "model", governed_provider + governed_input = ProviderHandler.requests[-1]["body"]["input"] + assert "Release Brief Source Fixture" in governed_input, governed_input + assert "file-backed fixture" in governed_input, governed_input + assert "evidence" in governed_input, governed_input + assert "input_files" in governed_input, governed_input + assert "output contract" in governed_input, governed_input + assert "rollback boundary" in governed_input, governed_input + assert "trust report" in governed_input, governed_input + assert "reports/output_quality_scorecard.md" in governed_input, governed_input + assert "missing evidence" in governed_input, governed_input + + chat_provider_url = f"http://127.0.0.1:{server.server_port}/chat/completions" + direct_chat_provider_proc = subprocess.run( + [ + sys.executable, + str(PROVIDER_RUNNER), + "--provider", + "deepseek", + "--api-format", + "chat-completions", + "--thinking", + "disabled", + "--base-url", + chat_provider_url, + "--allow-insecure-localhost", + "--model", + "fixture-model", + ], + cwd=ROOT, + input=json.dumps(provider_request), + capture_output=True, + text=True, + env=provider_env, + check=True, + ) + direct_chat_provider = json.loads(direct_chat_provider_proc.stdout) + assert direct_chat_provider["execution_kind"] == "model", direct_chat_provider + assert direct_chat_provider["provider"] == "deepseek", direct_chat_provider + assert direct_chat_provider["model"] == "fixture-model", direct_chat_provider + assert direct_chat_provider["usage"]["estimated"] is False, direct_chat_provider + assert ProviderHandler.requests[-1]["path"] == "/chat/completions", ProviderHandler.requests[-1] + assert ProviderHandler.requests[-1]["body"]["model"] == "fixture-model", ProviderHandler.requests[-1] + assert ProviderHandler.requests[-1]["body"]["thinking"]["type"] == "disabled", ProviderHandler.requests[-1] + assert ProviderHandler.requests[-1]["body"]["temperature"] == 0.0, ProviderHandler.requests[-1] + assert "Create a reusable skill package." in ProviderHandler.requests[-1]["body"]["messages"][0]["content"], ( + ProviderHandler.requests[-1] + ) + + provider_cases = tmp_root / "provider_cases.jsonl" + provider_cases.write_text( + json.dumps( + { + "id": "provider-contract", + "prompt": "Create a reusable skill package.", + "baseline_output": "", + "with_skill_output": "", + "assertions": [ + { + "id": "has-skill-entry", + "description": "Provider output includes the skill entrypoint.", + "required": ["SKILL.md"], + } + ], + }, + ensure_ascii=False, + ) + + "\n", + encoding="utf-8", + ) + provider_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(provider_cases), + "--output-json", + str(tmp_root / "provider.json"), + "--output-md", + str(tmp_root / "provider.md"), + "--runner-command", + json.dumps( + [ + sys.executable, + str(PROVIDER_RUNNER), + "--base-url", + provider_url, + "--allow-insecure-localhost", + "--model", + "fixture-model", + ] + ), + ], + cwd=ROOT, + capture_output=True, + text=True, + env=provider_env, + check=True, + ) + provider = json.loads(provider_proc.stdout) + assert provider["ok"], provider + assert provider["summary"]["variant_run_count"] == 2, provider + assert provider["summary"]["model_executed_count"] == 2, provider + assert provider["summary"]["token_observed_count"] == 2, provider + assert provider["summary"]["token_estimated_count"] == 0, provider + assert all(item["provider"] == "openai" for item in provider["runs"]), provider + assert all(item["model"] == "fixture-model" for item in provider["runs"]), provider + finally: + server.shutdown() + thread.join(timeout=2) + + bad_runner = tmp_root / "bad_runner.py" + bad_runner.write_text("import sys\nsys.exit(3)\n", encoding="utf-8") + bad_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(tmp_root / "bad.json"), + "--output-md", + str(tmp_root / "bad.md"), + "--runner-command", + json.dumps([sys.executable, str(bad_runner)]), + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + assert bad_proc.returncode == 2, bad_proc.stdout + bad = json.loads(bad_proc.stdout) + assert not bad["ok"], bad + assert bad["summary"]["failure_count"] == 10, bad + assert bad["failures"], bad + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_output_review_adjudication.py b/tests/verify_output_review_adjudication.py new file mode 100644 index 0000000..45f35f6 --- /dev/null +++ b/tests/verify_output_review_adjudication.py @@ -0,0 +1,599 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "scripts")) + +from output_review_privacy import BLOCKED_DECISION_FIELDS # noqa: E402 + +OUTPUT_EVAL = ROOT / "scripts" / "run_output_eval.py" +ADJUDICATOR = ROOT / "scripts" / "adjudicate_output_review.py" +IMPORTER = ROOT / "scripts" / "import_output_review_decisions.py" +CLI = ROOT / "scripts" / "yao.py" + + +def run(args: list[str], check: bool = True) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, *args], + cwd=ROOT, + capture_output=True, + text=True, + check=check, + ) + + +def write_json(path: Path, payload: dict) -> None: + path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + + +def adjudicate_payload( + tmp_root: Path, + blind_pack_json: Path, + answer_key_json: Path, + name: str, + decisions: dict, +) -> tuple[subprocess.CompletedProcess[str], dict]: + decisions_path = tmp_root / f"{name}_decisions.json" + write_json(decisions_path, decisions) + proc = run( + [ + str(ADJUDICATOR), + "--blind-pack", + str(blind_pack_json), + "--answer-key", + str(answer_key_json), + "--decisions", + str(decisions_path), + "--output-json", + str(tmp_root / f"{name}_adjudication.json"), + "--output-md", + str(tmp_root / f"{name}_adjudication.md"), + ], + check=False, + ) + return proc, json.loads(proc.stdout) + + +def main() -> None: + assert {"api_key", "raw_provider_prompt", "expected_winner_role"} <= BLOCKED_DECISION_FIELDS + + tmp_root = ROOT / "tests" / "tmp_output_review_adjudication" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + + scorecard_json = tmp_root / "output_quality_scorecard.json" + scorecard_md = tmp_root / "output_quality_scorecard.md" + blind_pack_json = tmp_root / "output_blind_review_pack.json" + blind_pack_md = tmp_root / "output_blind_review_pack.md" + answer_key_json = tmp_root / "output_blind_answer_key.json" + + run( + [ + str(OUTPUT_EVAL), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(scorecard_json), + "--output-md", + str(scorecard_md), + "--blind-pack-json", + str(blind_pack_json), + "--blind-pack-md", + str(blind_pack_md), + "--blind-answer-key-json", + str(answer_key_json), + ] + ) + + pending_json = tmp_root / "pending_adjudication.json" + pending_md = tmp_root / "pending_adjudication.md" + pending_proc = run( + [ + str(ADJUDICATOR), + "--blind-pack", + str(blind_pack_json), + "--answer-key", + str(answer_key_json), + "--decisions", + str(tmp_root / "missing_decisions.json"), + "--output-json", + str(pending_json), + "--output-md", + str(pending_md), + ] + ) + pending_payload = json.loads(pending_proc.stdout) + assert pending_payload["ok"], pending_payload + assert pending_payload["summary"]["pair_count"] == 5, pending_payload + assert pending_payload["summary"]["judgment_count"] == 0, pending_payload + assert pending_payload["summary"]["pending_count"] == 5, pending_payload + assert pending_payload["summary"]["agreement_rate"] is None, pending_payload + assert pending_payload["summary"]["needs_review"], pending_payload + assert pending_payload["summary"]["answer_revealed_count"] == 0, pending_payload + assert pending_payload["summary"]["pending_answer_hidden_count"] == 5, pending_payload + assert pending_payload["summary"]["reviewer_checklist_count"] == 5, pending_payload + assert pending_payload["summary"]["reviewer_checklist_pending_count"] == 5, pending_payload + assert pending_payload["summary"]["reviewer_checklist_ready_count"] == 0, pending_payload + assert pending_payload["summary"]["reviewer_checklist_invalid_count"] == 0, pending_payload + assert pending_payload["summary"]["reviewer_metadata_present"] is False, pending_payload + assert pending_payload["summary"]["blind_review_attested"] is False, pending_payload + assert pending_payload["summary"]["raw_content_excluded_attested"] is False, pending_payload + assert pending_payload["summary"]["reason_required"] is True, pending_payload + assert pending_payload["summary"]["ready_for_human_evidence"] is False, pending_payload + assert all(not item["expected_winner_variant"] and not item["expected_revealed"] for item in pending_payload["pairs"]), pending_payload + assert all("prompt" not in item and len(item["prompt_sha256"]) == 64 for item in pending_payload["pairs"]), pending_payload + checklist = {item["case_id"]: item for item in pending_payload["reviewer_checklist"]} + assert checklist["skill-package-contract"]["readiness"] == "awaiting-decision", checklist["skill-package-contract"] + assert checklist["skill-package-contract"]["answer_key_visible"] is False, checklist["skill-package-contract"] + assert "prompt" not in checklist["skill-package-contract"] and len(checklist["skill-package-contract"]["prompt_sha256"]) == 64, checklist["skill-package-contract"] + assert checklist["skill-package-contract"]["decisions_path"].endswith("tests/tmp_output_review_adjudication/missing_decisions.json"), checklist["skill-package-contract"] + assert checklist["skill-package-contract"]["commands"]["write_template"] == "python3 scripts/adjudicate_output_review.py --write-template", checklist["skill-package-contract"] + assert checklist["skill-package-contract"]["commands"]["import_decisions"].startswith("python3 scripts/yao.py output-review-import"), checklist["skill-package-contract"] + assert checklist["skill-package-contract"]["required_fields"]["winner_variant"].startswith("A or B"), checklist["skill-package-contract"] + assert checklist["skill-package-contract"]["required_fields"]["reason"].startswith("Required rationale"), checklist["skill-package-contract"] + assert "reviewer" in checklist["skill-package-contract"]["required_fields"], checklist["skill-package-contract"] + assert "reviewed_at" in checklist["skill-package-contract"]["required_fields"], checklist["skill-package-contract"] + assert "No reviewer decisions recorded yet" in pending_md.read_text(encoding="utf-8"), pending_md + pending_text = pending_md.read_text(encoding="utf-8") + assert "| skill-package-contract | pending | hidden | pending |" in pending_text, pending_text + assert "| skill-package-contract | pending | A | pending |" not in pending_text, pending_text + assert "Reviewer Checklist" in pending_text, pending_text + assert "Reviewer checklist: `0` ready / `5` total" in pending_text, pending_text + assert "answer key visible: `false`" in pending_text, pending_text + assert "Ready for human evidence: `false`" in pending_text, pending_text + + template_path = tmp_root / "output_review_decisions.json" + template_proc = run( + [ + str(ADJUDICATOR), + "--blind-pack", + str(blind_pack_json), + "--answer-key", + str(answer_key_json), + "--decisions", + str(template_path), + "--output-json", + str(tmp_root / "template_adjudication.json"), + "--output-md", + str(tmp_root / "template_adjudication.md"), + "--write-template", + ] + ) + template_payload = json.loads(template_proc.stdout) + assert template_payload["ok"], template_payload + assert template_payload["template_written"], template_payload + template = json.loads(template_path.read_text(encoding="utf-8")) + assert len(template["decisions"]) == 5, template + assert all(item["winner_variant"] == "" for item in template["decisions"]), template + assert len(template["review_integrity"]["blind_pack_sha256"]) == 64, template + assert template["review_integrity"]["case_count"] == 5, template + assert template["reviewer_attestation"]["blind_review_completed_before_answer_key"] is False, template + assert template["reviewer_attestation"]["answer_key_not_opened_before_decisions"] is False, template + + answer_key = json.loads(answer_key_json.read_text(encoding="utf-8")) + reviewer_attestation = { + "blind_review_completed_before_answer_key": True, + "answer_key_not_opened_before_decisions": True, + "raw_content_excluded": True, + "reviewer_reason_required": True, + } + decisions = { + "schema_version": "1.0", + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "reviewer_attestation": reviewer_attestation, + "decisions": [ + { + "case_id": item["case_id"], + "winner_variant": item["expected_winner_variant"], + "confidence": 0.9, + "reason": "Matches the rubric better.", + } + for item in answer_key["answers"] + ], + } + filled_path = tmp_root / "filled_decisions.json" + filled_path.write_text(json.dumps(decisions, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + filled_proc = run( + [ + str(ADJUDICATOR), + "--blind-pack", + str(blind_pack_json), + "--answer-key", + str(answer_key_json), + "--decisions", + str(filled_path), + "--output-json", + str(tmp_root / "filled_adjudication.json"), + "--output-md", + str(tmp_root / "filled_adjudication.md"), + ] + ) + filled_payload = json.loads(filled_proc.stdout) + assert filled_payload["ok"], filled_payload + assert filled_payload["summary"]["judgment_count"] == 5, filled_payload + assert filled_payload["summary"]["pending_count"] == 0, filled_payload + assert filled_payload["summary"]["agreement_count"] == 5, filled_payload + assert filled_payload["summary"]["agreement_rate"] == 100.0, filled_payload + assert filled_payload["summary"]["answer_revealed_count"] == 5, filled_payload + assert filled_payload["summary"]["pending_answer_hidden_count"] == 0, filled_payload + assert filled_payload["summary"]["reviewer_checklist_ready_count"] == 5, filled_payload + assert filled_payload["summary"]["reviewer_checklist_pending_count"] == 0, filled_payload + assert filled_payload["summary"]["reviewer_metadata_present"] is True, filled_payload + assert filled_payload["summary"]["blind_review_attested"] is True, filled_payload + assert filled_payload["summary"]["raw_content_excluded_attested"] is True, filled_payload + assert filled_payload["summary"]["ready_for_human_evidence"] is True, filled_payload + assert len(filled_payload["review_integrity"]["blind_pack_sha256"]) == 64, filled_payload + assert all(item["status"] == "match" for item in filled_payload["pairs"]), filled_payload + assert all(item["expected_winner_variant"] in {"A", "B"} and item["expected_revealed"] for item in filled_payload["pairs"]), filled_payload + assert all("prompt" not in item and len(item["prompt_sha256"]) == 64 for item in filled_payload["pairs"]), filled_payload + filled_checklist = {item["case_id"]: item for item in filled_payload["reviewer_checklist"]} + assert all(item["readiness"] == "adjudicated" and item["answer_key_visible"] and "prompt" not in item and len(item["prompt_sha256"]) == 64 for item in filled_checklist.values()), filled_checklist + + import_source = tmp_root / "reviewer_source.json" + import_source.write_text(json.dumps(decisions, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + imported_path = tmp_root / "imported_decisions.json" + imported_proc = run( + [ + str(IMPORTER), + "--input", + str(import_source), + "--blind-pack", + str(blind_pack_json), + "--output-json", + str(imported_path), + "--run-adjudication", + "--answer-key", + str(answer_key_json), + "--adjudication-json", + str(tmp_root / "imported_adjudication.json"), + "--adjudication-md", + str(tmp_root / "imported_adjudication.md"), + ] + ) + imported_payload = json.loads(imported_proc.stdout) + assert imported_payload["ok"], imported_payload + assert imported_payload["summary"]["canonical_written"] is True, imported_payload + assert imported_payload["summary"]["adjudication_run"] is True, imported_payload + assert imported_payload["summary"]["adjudication_pending_count"] == 0, imported_payload + imported_decisions = json.loads(imported_path.read_text(encoding="utf-8")) + assert imported_decisions["import_contract"]["raw_content_allowed"] is False, imported_decisions + assert imported_decisions["import_contract"]["answer_key_fields_allowed"] is False, imported_decisions + assert imported_decisions["import_contract"]["answer_key_opened_by_importer"] is False, imported_decisions + assert imported_decisions["import_contract"]["blind_review_attested"] is True, imported_decisions + assert imported_decisions["reviewer_attestation"]["blind_review_completed_before_answer_key"] is True, imported_decisions + assert len(imported_decisions["review_integrity"]["blind_pack_sha256"]) == 64, imported_decisions + assert imported_decisions["reviewer"] == "Yao QA", imported_decisions + + no_reason_source = tmp_root / "no_reason_source.json" + write_json( + no_reason_source, + { + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "decisions": [ + { + "case_id": answer_key["answers"][0]["case_id"], + "winner_variant": answer_key["answers"][0]["expected_winner_variant"], + "confidence": 0.8, + } + ], + }, + ) + no_reason_import_proc = run( + [ + str(IMPORTER), + "--input", + str(no_reason_source), + "--blind-pack", + str(blind_pack_json), + "--output-json", + str(tmp_root / "no_reason_decisions.json"), + ], + check=False, + ) + no_reason_import_payload = json.loads(no_reason_import_proc.stdout) + assert no_reason_import_proc.returncode == 2, no_reason_import_payload + assert any("reason is required for imported human decisions" in failure for failure in no_reason_import_payload["failures"]), no_reason_import_payload + assert not (tmp_root / "no_reason_decisions.json").exists(), no_reason_import_payload + + cli_import_proc = run( + [ + str(CLI), + "output-review-import", + "--input", + str(import_source), + "--blind-pack", + str(blind_pack_json), + "--output-json", + str(tmp_root / "cli_imported_decisions.json"), + "--run-adjudication", + "--answer-key", + str(answer_key_json), + "--adjudication-json", + str(tmp_root / "cli_imported_adjudication.json"), + "--adjudication-md", + str(tmp_root / "cli_imported_adjudication.md"), + ] + ) + cli_import_payload = json.loads(cli_import_proc.stdout) + assert cli_import_payload["ok"], cli_import_payload + assert cli_import_payload["summary"]["canonical_written"] is True, cli_import_payload + assert cli_import_payload["summary"]["adjudication_pending_count"] == 0, cli_import_payload + + jsonl_source = tmp_root / "reviewer_source.jsonl" + jsonl_source.write_text("\n".join(json.dumps(item, ensure_ascii=False) for item in decisions["decisions"]) + "\n", encoding="utf-8") + jsonl_proc = run([ + str(IMPORTER), "--input", str(jsonl_source), "--blind-pack", str(blind_pack_json), + "--output-json", str(tmp_root / "jsonl_decisions.json"), "--reviewer", "Yao QA", + "--reviewed-at", "2026-06-13", "--blind-review-attested", + ]) + jsonl_payload = json.loads(jsonl_proc.stdout) + assert jsonl_payload["ok"], jsonl_payload + assert jsonl_payload["summary"]["completed_decision_count"] == 5, jsonl_payload + jsonl_decisions = json.loads((tmp_root / "jsonl_decisions.json").read_text(encoding="utf-8")) + assert jsonl_decisions["reviewer_attestation"]["blind_review_completed_before_answer_key"] is True, jsonl_decisions + + csv_source = tmp_root / "reviewer_source.csv" + csv_source.write_text( + "case_id,winner_variant,confidence,reason\n" + + "\n".join( + f"{item['case_id']},{item['expected_winner_variant']},0.8,CSV reviewer choice" + for item in answer_key["answers"] + ) + + "\n", + encoding="utf-8", + ) + csv_proc = run([ + str(IMPORTER), "--input", str(csv_source), "--blind-pack", str(blind_pack_json), + "--output-json", str(tmp_root / "csv_decisions.json"), "--reviewer", "Yao QA", + "--reviewed-at", "2026-06-13", "--blind-review-attested", + ]) + csv_payload = json.loads(csv_proc.stdout) + assert csv_payload["ok"], csv_payload + assert csv_payload["summary"]["completed_decision_count"] == 5, csv_payload + + private_source = tmp_root / "private_source.json" + private_source.write_text( + json.dumps( + { + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "decisions": [ + { + "case_id": answer_key["answers"][0]["case_id"], + "winner_variant": "A", + "prompt": "raw prompt must not be imported", + } + ], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + private_proc = run( + [ + str(IMPORTER), + "--input", + str(private_source), + "--blind-pack", + str(blind_pack_json), + "--output-json", + str(tmp_root / "private_decisions.json"), + ], + check=False, + ) + private_payload = json.loads(private_proc.stdout) + assert private_proc.returncode == 2, private_payload + assert private_payload["ok"] is False, private_payload + assert any("forbidden raw or answer-key fields" in failure for failure in private_payload["failures"]), private_payload + assert not (tmp_root / "private_decisions.json").exists(), private_payload + + nested_private_source = tmp_root / "nested_private_source.json" + nested_private_source.write_text( + json.dumps( + { + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "decisions": [ + { + "case_id": answer_key["answers"][0]["case_id"], + "winner_variant": "A", + "metadata": {"Raw_Output": "nested raw output must not be imported"}, + } + ], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + nested_private_proc = run( + [ + str(IMPORTER), + "--input", + str(nested_private_source), + "--blind-pack", + str(blind_pack_json), + "--output-json", + str(tmp_root / "nested_private_decisions.json"), + ], + check=False, + ) + nested_private_payload = json.loads(nested_private_proc.stdout) + assert nested_private_proc.returncode == 2, nested_private_payload + assert nested_private_payload["ok"] is False, nested_private_payload + assert any("decision #1.metadata.Raw_Output" in failure for failure in nested_private_payload["failures"]), nested_private_payload + assert not (tmp_root / "nested_private_decisions.json").exists(), nested_private_payload + + credential_source = tmp_root / "credential_source.json" + credential_source.write_text( + json.dumps( + { + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "decisions": [ + { + "case_id": answer_key["answers"][0]["case_id"], + "winner_variant": "A", + "metadata": {"API_KEY": "credential material must not be imported"}, + } + ], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + credential_proc = run( + [ + str(IMPORTER), + "--input", + str(credential_source), + "--blind-pack", + str(blind_pack_json), + "--output-json", + str(tmp_root / "credential_decisions.json"), + ], + check=False, + ) + credential_payload = json.loads(credential_proc.stdout) + assert credential_proc.returncode == 2, credential_payload + assert credential_payload["ok"] is False, credential_payload + assert any("decision #1.metadata.API_KEY" in failure for failure in credential_payload["failures"]), credential_payload + assert not (tmp_root / "credential_decisions.json").exists(), credential_payload + + unknown_case = tmp_root / "unknown_case.json" + unknown_case.write_text( + json.dumps( + { + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "decisions": [{"case_id": "unknown", "winner_variant": "A"}], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + unknown_proc = run( + [ + str(IMPORTER), + "--input", + str(unknown_case), + "--blind-pack", + str(blind_pack_json), + "--output-json", + str(tmp_root / "unknown_decisions.json"), + ], + check=False, + ) + unknown_payload = json.loads(unknown_proc.stdout) + assert unknown_proc.returncode == 2, unknown_payload + assert any("unknown case_id" in failure for failure in unknown_payload["failures"]), unknown_payload + + invalid = { + "schema_version": "1.0", + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "decisions": [ + { + "case_id": answer_key["answers"][0]["case_id"], + "winner_variant": "C", + "confidence": 0.9, + "reason": "Invalid variant should fail validation.", + } + ], + } + invalid_path = tmp_root / "invalid_decisions.json" + invalid_path.write_text(json.dumps(invalid, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + invalid_proc = run( + [ + str(ADJUDICATOR), + "--blind-pack", + str(blind_pack_json), + "--answer-key", + str(answer_key_json), + "--decisions", + str(invalid_path), + "--output-json", + str(tmp_root / "invalid_adjudication.json"), + "--output-md", + str(tmp_root / "invalid_adjudication.md"), + ], + check=False, + ) + assert invalid_proc.returncode == 2, invalid_proc.stdout + invalid_payload = json.loads(invalid_proc.stdout) + assert not invalid_payload["ok"], invalid_payload + assert invalid_payload["summary"]["invalid_decision_count"] == 1, invalid_payload + assert invalid_payload["summary"]["answer_revealed_count"] == 0, invalid_payload + assert invalid_payload["summary"]["reviewer_checklist_invalid_count"] == 1, invalid_payload + assert invalid_payload["pairs"][0]["expected_winner_variant"] == "", invalid_payload + assert invalid_payload["pairs"][0]["expected_revealed"] is False, invalid_payload + invalid_checklist = {item["case_id"]: item for item in invalid_payload["reviewer_checklist"]} + assert invalid_checklist[answer_key["answers"][0]["case_id"]]["readiness"] == "fix-decision", invalid_checklist + assert invalid_checklist[answer_key["answers"][0]["case_id"]]["answer_key_visible"] is False, invalid_checklist + assert invalid_payload["failures"], invalid_payload + + no_reason_direct = { + "schema_version": "1.0", + "reviewer": "Yao QA", + "reviewed_at": "2026-06-13", + "decisions": [ + { + "case_id": answer_key["answers"][0]["case_id"], + "winner_variant": answer_key["answers"][0]["expected_winner_variant"], + "confidence": 0.9, + "reason": "", + } + ], + } + no_reason_proc, no_reason_payload = adjudicate_payload( + tmp_root, blind_pack_json, answer_key_json, "no_reason", no_reason_direct + ) + assert no_reason_proc.returncode == 2, no_reason_proc.stdout + assert no_reason_payload["summary"]["invalid_decision_count"] == 1, no_reason_payload + assert no_reason_payload["summary"]["answer_revealed_count"] == 0, no_reason_payload + assert no_reason_payload["summary"]["ready_for_human_evidence"] is False, no_reason_payload + assert no_reason_payload["pairs"][0]["expected_winner_variant"] == "", no_reason_payload + assert no_reason_payload["pairs"][0]["expected_revealed"] is False, no_reason_payload + assert any("reason is required before answer key can be revealed" in failure for failure in no_reason_payload["failures"]), no_reason_payload + + missing_metadata = { + "schema_version": "1.0", + "decisions": [ + { + "case_id": answer_key["answers"][0]["case_id"], + "winner_variant": answer_key["answers"][0]["expected_winner_variant"], + "confidence": 0.9, + "reason": "Visible rubric-based rationale.", + } + ], + } + missing_metadata_proc, missing_metadata_payload = adjudicate_payload( + tmp_root, blind_pack_json, answer_key_json, "missing_metadata", missing_metadata + ) + assert missing_metadata_proc.returncode == 2, missing_metadata_proc.stdout + assert missing_metadata_payload["summary"]["reviewer_metadata_present"] is False, missing_metadata_payload + assert missing_metadata_payload["summary"]["invalid_decision_count"] == 1, missing_metadata_payload + assert missing_metadata_payload["summary"]["answer_revealed_count"] == 0, missing_metadata_payload + assert missing_metadata_payload["summary"]["ready_for_human_evidence"] is False, missing_metadata_payload + assert any("reviewer and reviewed_at are required" in failure for failure in missing_metadata_payload["failures"]), missing_metadata_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_output_review_kit.py b/tests/verify_output_review_kit.py new file mode 100644 index 0000000..e8d0cd3 --- /dev/null +++ b/tests/verify_output_review_kit.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +OUTPUT_EVAL = ROOT / "scripts" / "run_output_eval.py" +REVIEW_KIT = ROOT / "scripts" / "prepare_output_review_kit.py" +CLI = ROOT / "scripts" / "yao.py" + + +def run(args: list[str], check: bool = True) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, *args], + cwd=ROOT, + capture_output=True, + text=True, + check=check, + ) + + +def assert_no_answer_key_leak(text: str) -> None: + forbidden = [ + "output_blind_answer_key", + "expected_winner", + "variant_a_role", + "variant_b_role", + "score_winner_role", + ] + for needle in forbidden: + assert needle not in text, f"review kit leaked hidden field {needle!r}" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_output_review_kit" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + + scorecard_json = tmp_root / "output_quality_scorecard.json" + scorecard_md = tmp_root / "output_quality_scorecard.md" + blind_pack_json = tmp_root / "output_blind_review_pack.json" + blind_pack_md = tmp_root / "output_blind_review_pack.md" + answer_key_json = tmp_root / "output_blind_answer_key.json" + + run( + [ + str(OUTPUT_EVAL), + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(scorecard_json), + "--output-md", + str(scorecard_md), + "--blind-pack-json", + str(blind_pack_json), + "--blind-pack-md", + str(blind_pack_md), + "--blind-answer-key-json", + str(answer_key_json), + ] + ) + + decisions_path = tmp_root / "output_review_decisions.json" + kit_json = tmp_root / "output_review_kit.json" + kit_md = tmp_root / "output_review_kit.md" + kit_html = tmp_root / "output_review_kit.html" + pending_proc = run( + [ + str(REVIEW_KIT), + "--blind-pack-json", + str(blind_pack_json), + "--blind-pack-md", + str(blind_pack_md), + "--decisions", + str(decisions_path), + "--output-json", + str(kit_json), + "--output-md", + str(kit_md), + "--output-html", + str(kit_html), + "--write-template", + ] + ) + pending_payload = json.loads(pending_proc.stdout) + assert pending_payload["ok"], pending_payload + assert pending_payload["template_written"], pending_payload + assert pending_payload["summary"]["case_count"] == 5, pending_payload + assert pending_payload["summary"]["pending_decision_count"] == 5, pending_payload + assert pending_payload["summary"]["ready_for_adjudication_count"] == 0, pending_payload + assert pending_payload["summary"]["answer_key_hidden"] is True, pending_payload + assert pending_payload["summary"]["answer_key_path_exposed"] is False, pending_payload + assert pending_payload["summary"]["ready_to_run_adjudication"] is False, pending_payload + assert pending_payload["review_contract"]["required_fields"]["reason"].startswith("Required rationale"), pending_payload + assert "answer_key" not in pending_payload["artifacts"], pending_payload["artifacts"] + assert pending_payload["artifacts"]["reviewer_kit_html"].endswith("tests/tmp_output_review_kit/output_review_kit.html"), pending_payload["artifacts"] + assert len(pending_payload["cases"]) == 5, pending_payload["cases"] + first_case = pending_payload["cases"][0] + assert first_case["variant_a"]["output"], first_case + assert first_case["variant_b"]["output"], first_case + assert first_case["decision_state"]["status"] == "awaiting-decision", first_case + assert decisions_path.exists(), decisions_path + + kit_json_text = kit_json.read_text(encoding="utf-8") + kit_md_text = kit_md.read_text(encoding="utf-8") + kit_html_text = kit_html.read_text(encoding="utf-8") + assert "# Output Review Kit" in kit_md_text, kit_md_text + assert "answer key path exposed: `false`" in kit_md_text, kit_md_text + assert "Variant A" in kit_md_text and "Variant B" in kit_md_text, kit_md_text + assert "python3 scripts/yao.py output-review" in kit_md_text, kit_md_text + assert "Output Review Kit" in kit_html_text, kit_html_text + assert "Reviewer cockpit for output quality decisions" in kit_html_text, kit_html_text + assert "Ready0" in kit_html_text, kit_html_text + assert "Invalid0" in kit_html_text, kit_html_text + assert "Variant A" in kit_html_text and "Variant B" in kit_html_text, kit_html_text + assert "Decision Template" in kit_html_text, kit_html_text + assert_no_answer_key_leak(kit_json_text) + assert_no_answer_key_leak(kit_md_text) + assert_no_answer_key_leak(kit_html_text) + + cli_proc = run( + [ + str(CLI), + "output-review-kit", + "--blind-pack-json", + str(blind_pack_json), + "--blind-pack-md", + str(blind_pack_md), + "--decisions", + str(decisions_path), + "--output-json", + str(tmp_root / "cli_output_review_kit.json"), + "--output-md", + str(tmp_root / "cli_output_review_kit.md"), + "--output-html", + str(tmp_root / "cli_output_review_kit.html"), + ] + ) + cli_payload = json.loads(cli_proc.stdout) + assert cli_payload["ok"], cli_payload + assert cli_payload["summary"]["case_count"] == 5, cli_payload + assert cli_payload["summary"]["answer_key_hidden"] is True, cli_payload + assert "answer_key" not in cli_payload["artifacts"], cli_payload["artifacts"] + assert (tmp_root / "cli_output_review_kit.html").exists(), cli_payload + + template = json.loads(decisions_path.read_text(encoding="utf-8")) + filled = { + "schema_version": "1.0", + "reviewer": "Yao QA", + "reviewed_at": "2026-06-14", + "decisions": [ + { + "case_id": item["case_id"], + "winner_variant": "A", + "confidence": 0.8, + "reason": "Variant A is clearer against the visible rubric.", + } + for item in template["decisions"] + ], + } + decisions_path.write_text(json.dumps(filled, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + ready_proc = run( + [ + str(REVIEW_KIT), + "--blind-pack-json", + str(blind_pack_json), + "--blind-pack-md", + str(blind_pack_md), + "--decisions", + str(decisions_path), + "--output-json", + str(tmp_root / "ready_output_review_kit.json"), + "--output-md", + str(tmp_root / "ready_output_review_kit.md"), + "--output-html", + str(tmp_root / "ready_output_review_kit.html"), + ] + ) + ready_payload = json.loads(ready_proc.stdout) + assert ready_payload["ok"], ready_payload + assert ready_payload["summary"]["ready_for_adjudication_count"] == 5, ready_payload + assert ready_payload["summary"]["pending_decision_count"] == 0, ready_payload + assert ready_payload["summary"]["invalid_decision_count"] == 0, ready_payload + assert ready_payload["summary"]["reviewer_metadata_present"] is True, ready_payload + assert ready_payload["summary"]["ready_to_run_adjudication"] is True, ready_payload + assert all(case["decision_state"]["status"] == "ready-for-adjudication" for case in ready_payload["cases"]), ready_payload + + invalid = dict(filled) + invalid["decisions"] = [dict(filled["decisions"][0], winner_variant="C", reason="")] + invalid_path = tmp_root / "invalid_output_review_decisions.json" + invalid_path.write_text(json.dumps(invalid, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + invalid_proc = run( + [ + str(REVIEW_KIT), + "--blind-pack-json", + str(blind_pack_json), + "--blind-pack-md", + str(blind_pack_md), + "--decisions", + str(invalid_path), + "--output-json", + str(tmp_root / "invalid_output_review_kit.json"), + "--output-md", + str(tmp_root / "invalid_output_review_kit.md"), + "--output-html", + str(tmp_root / "invalid_output_review_kit.html"), + ] + ) + invalid_payload = json.loads(invalid_proc.stdout) + assert invalid_payload["ok"], invalid_payload + assert invalid_payload["summary"]["invalid_decision_count"] == 1, invalid_payload + assert invalid_payload["summary"]["answer_key_hidden"] is True, invalid_payload + assert invalid_payload["cases"][0]["decision_state"]["status"] == "needs-fix", invalid_payload["cases"][0] + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_output_risk_profile.py b/tests/verify_output_risk_profile.py new file mode 100644 index 0000000..6f1ec2f --- /dev/null +++ b/tests/verify_output_risk_profile.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_output_risk_profile" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "tutorial-risk-demo", + "--description", + "Create Markdown tutorials with screenshots, tables, citations, and personalized section headings.", + "--output-dir", + str(tmp_root), + "--intent-primary-output", + "A polished Markdown tutorial.", + "--intent-real-input", + "screenshots", + "--intent-real-input", + "source notes", + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = tmp_root / "tutorial-risk-demo" + proc = subprocess.run( + [sys.executable, str(ROOT / "scripts" / "render_output_risk_profile.py"), str(skill_dir)], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + summary = payload["summary"] + keys = {item["key"] for item in summary["risk_families"]} + assert "tutorial_quality" in keys, summary + assert "markdown_readability" in keys, summary + assert summary["output_constraints"], summary + assert summary["self_repair_checks"], summary + markdown = (skill_dir / "reports" / "output-risk-profile.md").read_text(encoding="utf-8") + assert "Likely Output Mistakes" in markdown, markdown[:800] + assert "Self-Repair Checks" in markdown, markdown[:1200] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_package_verification.py b/tests/verify_package_verification.py new file mode 100644 index 0000000..45eed49 --- /dev/null +++ b/tests/verify_package_verification.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +import tempfile +import zipfile +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_package_verification" +PACKAGER = ROOT / "scripts" / "cross_packager.py" +VERIFIER = ROOT / "scripts" / "verify_package.py" +EXPECTATIONS = ROOT / "evals" / "packaging_expectations.json" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = {} + if proc.stdout.strip(): + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def build_package(out_dir: Path, skill_root: Path = ROOT) -> dict: + return run( + [ + sys.executable, + str(PACKAGER), + str(skill_root), + "--platform", + "openai", + "--platform", + "claude", + "--platform", + "generic", + "--platform", + "vscode", + "--expectations", + str(EXPECTATIONS), + "--output-dir", + str(out_dir), + "--zip", + ] + ) + + +def verify_package(out_dir: Path, output_json: Path, output_md: Path, skill_root: Path = ROOT) -> dict: + return run( + [ + sys.executable, + str(VERIFIER), + str(skill_root), + "--package-dir", + str(out_dir), + "--expectations", + str(EXPECTATIONS), + "--registry-json", + str(ROOT / "reports" / "registry_audit.json"), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--require-zip", + "--generated-at", + "2026-06-13", + ] + ) + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + + valid_dir = TMP / "dist" + build = build_package(valid_dir) + assert build["ok"], build + valid = verify_package(valid_dir, TMP / "package_verification.json", TMP / "package_verification.md") + payload = valid["payload"] + assert valid["ok"], valid + assert payload["ok"], payload + assert payload["summary"]["target_count"] == 4, payload + assert payload["summary"]["adapter_count"] == 4, payload + assert payload["summary"]["archive_present"], payload + assert payload["summary"]["archive_sha256"], payload + assert payload["summary"]["nested_skill_entry_count"] == 0, payload + assert not payload["failures"], payload + assert (TMP / "package_verification.md").exists(), TMP + with zipfile.ZipFile(valid_dir / "yao-meta-skill.zip") as archive: + skill_entries = sorted(name for name in archive.namelist() if name.endswith("/SKILL.md")) + assert skill_entries == ["yao-meta-skill/SKILL.md"], skill_entries + + with tempfile.TemporaryDirectory(prefix="renamed-package-root-") as temp_root: + renamed_root = Path(temp_root) / "checkout-alias" + shutil.copytree( + ROOT, + renamed_root, + ignore=shutil.ignore_patterns(".git", ".previews", "dist", "__pycache__", ".pytest_cache", "tmp*"), + ) + renamed_dir = TMP / "renamed-dist" + renamed_build = build_package(renamed_dir, renamed_root) + assert renamed_build["ok"], renamed_build + assert (renamed_dir / "yao-meta-skill.zip").exists(), renamed_build + with zipfile.ZipFile(renamed_dir / "yao-meta-skill.zip") as archive: + names = set(archive.namelist()) + assert "yao-meta-skill/SKILL.md" in names, sorted(list(names))[:10] + assert not [name for name in names if name.endswith("/SKILL.md") and name != "yao-meta-skill/SKILL.md"], names + renamed_valid = verify_package(renamed_dir, TMP / "renamed_package_verification.json", TMP / "renamed_package_verification.md", renamed_root) + assert renamed_valid["ok"], renamed_valid + + nested_skill_dir = TMP / "nested-skill-dist" + shutil.copytree(valid_dir, nested_skill_dir) + with zipfile.ZipFile(nested_skill_dir / "yao-meta-skill.zip", "a", compression=zipfile.ZIP_DEFLATED) as archive: + archive.writestr("yao-meta-skill/examples/demo/SKILL.md", "---\nname: demo\n---\n") + nested = verify_package(nested_skill_dir, TMP / "nested_skill.json", TMP / "nested_skill.md") + assert nested["returncode"] == 2, nested + nested_payload = nested["payload"] + assert nested_payload["summary"]["nested_skill_entry_count"] == 1, nested_payload + assert any("Archive exposes only the root SKILL.md entrypoint" in item for item in nested_payload["failures"]), nested_payload + + unsafe_dir = TMP / "unsafe-dist" + shutil.copytree(valid_dir, unsafe_dir) + with zipfile.ZipFile(unsafe_dir / "yao-meta-skill.zip", "a", compression=zipfile.ZIP_DEFLATED) as archive: + archive.writestr("../evil.txt", "bad") + unsafe = verify_package(unsafe_dir, TMP / "unsafe.json", TMP / "unsafe.md") + assert unsafe["returncode"] == 2, unsafe + unsafe_payload = unsafe["payload"] + assert not unsafe_payload["ok"], unsafe_payload + assert any("Archive has no absolute or parent-traversal entries" in item for item in unsafe_payload["failures"]), unsafe_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_packager_failures.py b/tests/verify_packager_failures.py new file mode 100644 index 0000000..be8f014 --- /dev/null +++ b/tests/verify_packager_failures.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "cross_packager.py" +EXPECTATIONS = ROOT / "evals" / "packaging_expectations.json" +TMP = ROOT / "tests" / "tmp" + + +def run_case(name: str, cmd: list[str], expected_substring: str) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = {} + if proc.stdout.strip(): + try: + payload = json.loads(proc.stdout) + except json.JSONDecodeError: + payload = {"raw_stdout": proc.stdout} + joined = proc.stdout + "\n" + proc.stderr + passed = proc.returncode == 2 and expected_substring in joined + return { + "name": name, + "passed": passed, + "returncode": proc.returncode, + "expected_substring": expected_substring, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def main() -> None: + TMP.mkdir(parents=True, exist_ok=True) + cases = [ + run_case( + "missing_interface_field", + [ + sys.executable, + str(SCRIPT), + str(ROOT / "tests" / "fixtures" / "package_missing_interface_field"), + "--platform", + "openai", + "--expectations", + str(EXPECTATIONS), + "--output-dir", + str(TMP / "missing_interface_field"), + ], + "Missing required interface fields", + ), + run_case( + "invalid_yaml", + [ + sys.executable, + str(SCRIPT), + str(ROOT / "tests" / "fixtures" / "package_invalid_yaml"), + "--platform", + "openai", + "--expectations", + str(EXPECTATIONS), + "--output-dir", + str(TMP / "invalid_yaml"), + ], + "while scanning a quoted scalar", + ), + run_case( + "unsupported_platform", + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--platform", + "bad_target", + "--expectations", + str(EXPECTATIONS), + "--output-dir", + str(TMP / "unsupported_platform"), + ], + "Unsupported platform", + ), + ] + report = {"ok": all(case["passed"] for case in cases), "cases": cases} + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_portability_report.py b/tests/verify_portability_report.py new file mode 100644 index 0000000..3e1a7d1 --- /dev/null +++ b/tests/verify_portability_report.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +import tempfile +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_portability_report.py" +sys.path.insert(0, str(ROOT / "scripts")) + +from render_portability_report import find_ir + + +def main() -> None: + with tempfile.TemporaryDirectory(prefix="renamed-skill-root-") as tmp: + tmp_root = Path(tmp) + ir_dir = tmp_root / "skill-ir" / "examples" + ir_dir.mkdir(parents=True) + ir_path = ir_dir / "yao-meta-skill.json" + ir_path.write_text( + json.dumps({"schema_version": "2.0.0", "job_to_be_done": "test renamed checkout"}), + encoding="utf-8", + ) + ir_payload, ir_source = find_ir(tmp_root) + assert ir_payload["schema_version"] == "2.0.0", ir_payload + assert ir_source == "skill-ir/examples/yao-meta-skill.json", ir_source + + payload = None + for args in ([], [str(ROOT)]): + proc = subprocess.run( + [sys.executable, str(SCRIPT), *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + if proc.returncode != 0: + print(proc.stdout) + print(proc.stderr) + raise SystemExit(proc.returncode) + current_payload = json.loads(proc.stdout) + if payload is None: + payload = current_payload + elif current_payload != payload: + print(json.dumps({"default": payload, "explicit": current_payload}, ensure_ascii=False, indent=2)) + raise SystemExit(2) + + assert payload is not None + failures = [] + if payload.get("score", 0) < 95: + failures.append(f"portability score too low: {payload.get('score')}") + if payload.get("summary", {}).get("adapter_target_count", 0) < 3: + failures.append("adapter target coverage too low") + if payload.get("summary", {}).get("degradation_coverage", 0) < 3: + failures.append("degradation coverage too low") + if payload.get("summary", {}).get("snapshot_count", 0) < 3: + failures.append("snapshot coverage too low") + + report = {"ok": not failures, "failures": failures, "payload": payload} + print(json.dumps(report, ensure_ascii=False, indent=2)) + if failures: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_promotion_checker.py b/tests/verify_promotion_checker.py new file mode 100644 index 0000000..b157361 --- /dev/null +++ b/tests/verify_promotion_checker.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def main() -> None: + proc = subprocess.run( + [sys.executable, "scripts/promotion_checker.py"], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + if proc.returncode != 0: + raise SystemExit(proc.returncode) + + summary = payload["summary"] + assert summary["blocked"] == 0, payload + assert summary["keep_current"] >= 1, payload + + targets = payload["targets"] + assert len(targets) >= 3, payload + + candidate_registry = json.loads((ROOT / "reports" / "candidate_registry.json").read_text(encoding="utf-8")) + assert len(candidate_registry["targets"]) == len(targets) + + for target in targets: + assert target["decision"] in {"promote", "keep_current", "blocked"} + assert target["promotion"]["gate_status"]["route_confusion_clean"] is True + assert target["promotion"]["gate_status"]["judge_blind_agreement"] is True + assert target["promotion"]["causes"], target + if target["winner_label"] == "Current": + assert target["decision"] == "keep_current", target + if target["name"] == "governed-incident-command": + assert "current_holdout_gap_present" in target["promotion"]["causes"], target + + bundle_dir = ROOT / "reports" / "iteration_bundles" / target["name"] + assert (bundle_dir / "bundle.json").exists(), bundle_dir + assert (bundle_dir / "bundle.md").exists(), bundle_dir + assert (bundle_dir / "review.md").exists(), bundle_dir + + registry_entries = target["registry"] + roles = {entry["role"] for entry in registry_entries} + assert "baseline" in roles and "current" in roles, target + assert any(entry["role"] == "candidate" for entry in registry_entries), target + + print(json.dumps({"ok": True, "summary": summary}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_prompt_quality_profile.py b/tests/verify_prompt_quality_profile.py new file mode 100644 index 0000000..e986373 --- /dev/null +++ b/tests/verify_prompt_quality_profile.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_prompt_quality_profile" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "prompt-quality-demo", + "--description", + "Create reusable RTF prompt templates that teach users how to analyze product feedback.", + "--output-dir", + str(tmp_root), + "--intent-job", + "Turn rough product-feedback goals into a reusable prompt and teaching workflow.", + "--intent-real-input", + "user goal", + "--intent-real-input", + "feedback examples", + "--intent-primary-output", + "A reusable RTF prompt contract with quality checks.", + "--intent-standard", + "clarity", + "--intent-standard", + "practicality", + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + + skill_dir = tmp_root / "prompt-quality-demo" + proc = subprocess.run( + [sys.executable, str(ROOT / "scripts" / "render_prompt_quality_profile.py"), str(skill_dir)], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + summary = payload["summary"] + markdown = Path(payload["artifacts"]["markdown"]).read_text(encoding="utf-8") + + assert payload["ok"], payload + assert summary["relevance"] == "prompt-heavy", summary + assert summary["primary_task_family"]["key"] == "prompt_engineering", summary + assert summary["complexity"]["band"] in {"medium", "complex", "expert"}, summary + assert set(summary["rtf_to_skill"].keys()) == {"role", "task", "format"}, summary + assert {item["key"] for item in summary["quality_matrix"]} == { + "completeness", + "clarity", + "consistency", + "practicality", + "specificity", + }, summary + assert summary["overall_quality_score"] >= 80, summary + assert "Prompt Quality Profile" in markdown, markdown[:300] + assert "Need Model" in markdown, markdown[:800] + assert "RTF To Skill Mapping" in markdown, markdown[:1200] + assert "Quality Matrix" in markdown, markdown[:1600] + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_python_compat_check.py b/tests/verify_python_compat_check.py new file mode 100644 index 0000000..353e29e --- /dev/null +++ b/tests/verify_python_compat_check.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "python_compat_check.py" +TMP = ROOT / "tests" / "tmp_python_compat" + + +def run_check(*extra: str, check: bool = True, cwd: Path = ROOT) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--generated-at", + "2026-06-14", + *extra, + ], + cwd=cwd, + capture_output=True, + text=True, + check=check, + ) + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + output_json = TMP / "python_compatibility.json" + output_md = TMP / "python_compatibility.md" + proc = run_check("--output-json", str(output_json), "--output-md", str(output_md)) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + assert payload["summary"]["target_python"] == "3.11", payload + assert payload["summary"]["file_count"] >= 50, payload["summary"] + assert payload["summary"]["issue_count"] == 0, payload["summary"] + assert payload["summary"]["fstring_311_violation_count"] == 0, payload["summary"] + markdown = output_md.read_text(encoding="utf-8") + assert "Python Compatibility" in markdown, markdown + assert "f-string 3.11 violations" in markdown, markdown + + safe = TMP / "safe.py" + safe.write_text( + "value = 'x|y'\n" + "escaped = value.replace('|', '\\\\|')\n" + "result = f'{escaped}'\n", + encoding="utf-8", + ) + safe_proc = run_check( + "--path", + safe.relative_to(ROOT).as_posix(), + "--output-json", + str(TMP / "safe_python_compatibility.json"), + "--output-md", + str(TMP / "safe_python_compatibility.md"), + check=True, + cwd=TMP, + ) + safe_payload = json.loads(safe_proc.stdout) + assert safe_payload["summary"]["file_count"] == 1, safe_payload + assert safe_payload["summary"]["issue_count"] == 0, safe_payload + + unsafe = TMP / "unsafe.py" + unsafe.write_text( + "value = 'x|y'\n" + "result = f\"{value.replace('|', '\\\\|')}\"\n", + encoding="utf-8", + ) + unsafe_proc = run_check( + "--path", + unsafe.relative_to(ROOT).as_posix(), + "--output-json", + str(TMP / "unsafe_python_compatibility.json"), + "--output-md", + str(TMP / "unsafe_python_compatibility.md"), + check=False, + cwd=TMP, + ) + assert unsafe_proc.returncode == 2, unsafe_proc.stdout + unsafe_payload = json.loads(unsafe_proc.stdout) + assert unsafe_payload["ok"] is False, unsafe_payload + assert unsafe_payload["summary"]["decision"] == "block-python-compat", unsafe_payload + assert unsafe_payload["summary"]["file_count"] == 1, unsafe_payload + assert unsafe_payload["summary"]["fstring_311_violation_count"] >= 1, unsafe_payload + assert any(item["rule"] == "fstring-expression-backslash" for item in unsafe_payload["issues"]), unsafe_payload["issues"] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_quality_checks.py b/tests/verify_quality_checks.py new file mode 100644 index 0000000..e9c1cca --- /dev/null +++ b/tests/verify_quality_checks.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path +from tempfile import TemporaryDirectory + + +ROOT = Path(__file__).resolve().parent.parent + + +def run(name: str, cmd: list[str], expect_ok: bool = True, expected_substrings: list[str] | None = None) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = {} + if proc.stdout.strip(): + try: + payload = json.loads(proc.stdout) + except json.JSONDecodeError: + payload = {"raw_stdout": proc.stdout} + + joined = proc.stdout + "\n" + proc.stderr + passed = proc.returncode == 0 if expect_ok else proc.returncode == 2 + if expected_substrings: + passed = passed and all(fragment in joined for fragment in expected_substrings) + + return { + "name": name, + "passed": passed, + "returncode": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def require_min_score(case: dict, minimum: int) -> None: + score = case.get("payload", {}).get("details", {}).get("governance_score") + case["minimum_score"] = minimum + case["observed_score"] = score + case["passed"] = case["passed"] and score is not None and score >= minimum + + +def require_context_targets(case: dict, max_initial: int, min_density: float) -> None: + stats = case.get("payload", {}).get("stats", {}) + initial = stats.get("estimated_initial_load_tokens") + density = stats.get("quality_density") + deferred = stats.get("deferred_resource_tokens") + deferred_threshold = stats.get("deferred_resource_warn_threshold") + deferred_dirs = stats.get("deferred_resource_dirs", []) + deferred_governance = stats.get("deferred_resource_governance", {}) + unused = stats.get("unused_resource_dirs", []) + case["max_initial_load"] = max_initial + case["observed_initial_load"] = initial + case["minimum_quality_density"] = min_density + case["observed_quality_density"] = density + case["observed_deferred_resource_tokens"] = deferred + case["deferred_resource_warn_threshold"] = deferred_threshold + case["deferred_resource_dir_count"] = len(deferred_dirs) if isinstance(deferred_dirs, list) else None + case["deferred_resource_governance_status"] = deferred_governance.get("status") if isinstance(deferred_governance, dict) else None + case["unused_resource_dirs"] = unused + case["passed"] = ( + case["passed"] + and initial is not None + and density is not None + and deferred is not None + and deferred_threshold is not None + and isinstance(deferred_dirs, list) + and isinstance(deferred_governance, dict) + and deferred_governance.get("status") in {"governed", "not-required", "needs-review"} + and initial <= max_initial + and density >= min_density + and not unused + ) + + +def verify_private_report_ignored() -> dict: + with TemporaryDirectory(prefix="yao-private-report-ignore-") as temp_dir: + skill_dir = Path(temp_dir) / "skill" + (skill_dir / "reports").mkdir(parents=True) + (skill_dir / "SKILL.md").write_text( + "---\n" + "name: report-ignore-fixture\n" + "description: Test fixture for ignored private reports.\n" + "---\n" + "Use this fixture to validate context sizing.\n", + encoding="utf-8", + ) + (skill_dir / "reports" / "kept.md").write_text("stable evidence\n", encoding="utf-8") + baseline = run( + "private_report_ignore_baseline", + [sys.executable, "scripts/context_sizer.py", str(skill_dir), "--json"], + ) + (skill_dir / "reports" / "client-research-plan.md").write_text( + "private notes\n" * 500, + encoding="utf-8", + ) + context_case = run( + "private_report_ignore_context_sizer", + [sys.executable, "scripts/context_sizer.py", str(skill_dir), "--json"], + ) + boundary_case = run( + "private_report_ignore_resource_boundary", + [sys.executable, "scripts/resource_boundary_check.py", str(skill_dir), "--max-initial-tokens", "5000"], + ) + + baseline_total = baseline.get("payload", {}).get("estimated_total_text_tokens") + context_total = context_case.get("payload", {}).get("estimated_total_text_tokens") + boundary_files = boundary_case.get("payload", {}).get("stats", {}).get("relevant_file_count") + passed = ( + baseline["passed"] + and context_case["passed"] + and boundary_case["passed"] + and baseline_total == context_total + and boundary_files == 2 + ) + return { + "name": "private_report_ignore", + "passed": passed, + "baseline_total_tokens": baseline_total, + "observed_total_tokens": context_total, + "resource_relevant_file_count": boundary_files, + "context_stdout": context_case["stdout"], + "boundary_stdout": boundary_case["stdout"], + } + + +def verify_volatile_report_outputs_ignored() -> dict: + with TemporaryDirectory(prefix="yao-volatile-report-ignore-") as temp_dir: + skill_dir = Path(temp_dir) / "skill" + (skill_dir / "reports").mkdir(parents=True) + (skill_dir / "SKILL.md").write_text( + "---\n" + "name: context-budget-ignore-fixture\n" + "description: Test fixture for self-generated context reports.\n" + "---\n" + "Use this fixture to validate context report idempotency.\n", + encoding="utf-8", + ) + (skill_dir / "reports" / "kept.md").write_text("stable evidence\n", encoding="utf-8") + baseline = run( + "volatile_report_ignore_baseline", + [sys.executable, "scripts/context_sizer.py", str(skill_dir), "--json"], + ) + volatile_reports = [ + "benchmark_reproducibility.json", + "benchmark_reproducibility.md", + "context_budget.json", + "context_budget_summary.json", + "context_budget.md", + "evidence_consistency.json", + "evidence_consistency.md", + "review-studio.html", + "review-studio.json", + "review-viewer.html", + "review-viewer.json", + "skill-interpretation.html", + "skill-interpretation.json", + "skill-overview.html", + "skill-overview.json", + "world_class_evidence_preflight.json", + "world_class_evidence_preflight.md", + "world_class_evidence_preflight.html", + ] + for report_name in volatile_reports: + report_path = skill_dir / "reports" / report_name + if report_path.suffix == ".json": + report_path.write_text( + json.dumps({"large": f"{report_name}\n" * 2000}, ensure_ascii=False), + encoding="utf-8", + ) + else: + report_path.write_text(f"{report_name}\n" * 2000, encoding="utf-8") + context_case = run( + "volatile_report_ignore_context_sizer", + [sys.executable, "scripts/context_sizer.py", str(skill_dir), "--json"], + ) + boundary_case = run( + "volatile_report_ignore_resource_boundary", + [sys.executable, "scripts/resource_boundary_check.py", str(skill_dir), "--max-initial-tokens", "5000"], + ) + + baseline_total = baseline.get("payload", {}).get("estimated_total_text_tokens") + context_total = context_case.get("payload", {}).get("estimated_total_text_tokens") + boundary_files = boundary_case.get("payload", {}).get("stats", {}).get("relevant_file_count") + passed = ( + baseline["passed"] + and context_case["passed"] + and boundary_case["passed"] + and baseline_total == context_total + and boundary_files == 2 + ) + return { + "name": "volatile_report_outputs_ignored", + "passed": passed, + "baseline_total_tokens": baseline_total, + "observed_total_tokens": context_total, + "resource_relevant_file_count": boundary_files, + "context_stdout": context_case["stdout"], + "boundary_stdout": boundary_case["stdout"], + } + + +def main() -> None: + python = sys.executable + cases = [] + + root_governance = run( + "root_governance", + [python, "scripts/governance_check.py", str(ROOT), "--require-manifest"], + ) + require_min_score(root_governance, 90) + cases.append(root_governance) + + root_resource = run( + "root_resource_boundaries", + [python, "scripts/resource_boundary_check.py", str(ROOT)], + ) + require_context_targets(root_resource, 1000, 100.0) + root_payload = root_resource.get("payload", {}) + root_governance_status = root_resource.get("deferred_resource_governance_status") + root_resource["passed"] = ( + root_resource["passed"] + and root_governance_status == "governed" + and not any("Deferred resource footprint is high" in item for item in root_payload.get("warnings", [])) + ) + cases.append(root_resource) + + complex_resource = run( + "complex_example_resource_boundaries", + [python, "scripts/resource_boundary_check.py", str(ROOT / "examples" / "complex-release-orchestrator" / "generated-skill")], + ) + require_context_targets(complex_resource, 1000, 120.0) + + governed_resource = run( + "governed_example_resource_boundaries", + [python, "scripts/resource_boundary_check.py", str(ROOT / "examples" / "governed-incident-command" / "generated-skill")], + ) + require_context_targets(governed_resource, 1000, 120.0) + + cases.extend([ + run( + "complex_example_governance", + [python, "scripts/governance_check.py", str(ROOT / "examples" / "complex-release-orchestrator" / "generated-skill"), "--require-manifest"], + ), + complex_resource, + run( + "invalid_governance_manifest", + [python, "scripts/governance_check.py", str(ROOT / "tests" / "fixtures" / "governance_invalid_manifest"), "--require-manifest"], + expect_ok=False, + expected_substrings=[ + "Missing manifest fields", + "Invalid status", + "updated_at must use YYYY-MM-DD", + "manifest name does not match", + ], + ), + ]) + + governed_example = run( + "governed_example_governance", + [python, "scripts/governance_check.py", str(ROOT / "examples" / "governed-incident-command" / "generated-skill"), "--require-manifest"], + ) + require_min_score(governed_example, 90) + cases.insert(4, governed_example) + cases.insert(5, governed_resource) + cases.append(verify_private_report_ignored()) + cases.append(verify_volatile_report_outputs_ignored()) + + report = {"ok": all(case["passed"] for case in cases), "cases": cases} + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_reference_scan.py b/tests/verify_reference_scan.py new file mode 100644 index 0000000..947574f --- /dev/null +++ b/tests/verify_reference_scan.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_reference_scan.py" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_reference_scan" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + init_proc = subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "reference-scan-demo", + "--description", + "Turn a product idea into a reusable benchmarked skill.", + "--output-dir", + str(tmp_root), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + init_payload = json.loads(init_proc.stdout) + created = Path(init_payload["root"]) + + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(created), + "--external-reference", + "Top Method::method::Borrow a tight benchmark loop.::Avoid copying source-specific branding.", + "--external-reference", + "Portable System::portability::Borrow neutral metadata and degradation rules.::Avoid target lock-in.", + "--user-reference", + "Beloved Product::taste::Learn the calm review experience and clear outcome framing.::Do not copy its copywriting.", + "--local-constraint", + "Current Team Naming::structure::Keep naming compatible with the current library.::Avoid inheriting private examples.", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + + md_path = Path(payload["artifacts"]["markdown"]) + json_path = Path(payload["artifacts"]["json"]) + assert md_path.exists(), md_path + assert json_path.exists(), json_path + + report_text = md_path.read_text(encoding="utf-8") + assert "# Reference Scan" in report_text, report_text[:200] + assert "Top Method" in report_text, report_text[:300] + assert "User-Supplied References" in report_text, report_text[:900] + assert "Beloved Product" in report_text, report_text[:1200] + assert "Local Fit Check" in report_text, report_text[:700] + assert "Borrow Plan" in report_text, report_text[:500] + + summary = json.loads(json_path.read_text(encoding="utf-8")) + assert len(summary["external_references"]) == 2, summary + assert len(summary["user_references"]) == 1, summary + assert len(summary["local_constraints"]) == 1, summary + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_reference_synthesis.py b/tests/verify_reference_synthesis.py new file mode 100644 index 0000000..9985c5d --- /dev/null +++ b/tests/verify_reference_synthesis.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" +BENCHMARK_FIXTURE_DIR = ROOT / "tests" / "fixtures" / "github_benchmark_scan" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_reference_synthesis" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + + proc = subprocess.run( + [ + sys.executable, + str(CLI), + "init", + "reference-synthesis-demo", + "--description", + "Turn repeated release notes into a portable governed release skill.", + "--output-dir", + str(tmp_root), + "--mode", + "governed", + "--archetype", + "governed", + "--github-query", + "release workflow evaluation portability", + "--github-fixture-dir", + str(BENCHMARK_FIXTURE_DIR), + "--user-reference", + "Minimal vibe helper::taste::Keep the first pass fast, minimal, and lightweight.::Do not add review, governance, or approval steps.", + "--intent-job", + "Turn repeated release notes into a portable governed release skill.", + "--intent-real-input", + "release notes", + "--intent-real-input", + "changelog snippets", + "--intent-primary-output", + "A governed release packet.", + "--intent-exclusion", + "Do not publish blog posts.", + "--intent-constraint", + "portability", + "--intent-standard", + "consistency", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + skill_dir = Path(payload["root"]) + synthesis_path = skill_dir / "reports" / "reference-synthesis.json" + assert synthesis_path.exists(), synthesis_path + synthesis = json.loads(synthesis_path.read_text(encoding="utf-8")) + assert len(synthesis["github_benchmarks"]) == 3, synthesis + assert len(synthesis["source_tracks"]) == 3, synthesis + assert synthesis["synthesis"]["borrow_now"], synthesis + assert synthesis["synthesis"]["recommendation"]["summary"], synthesis + assert synthesis["synthesis"]["pattern_gate"]["accepted"], synthesis + assert synthesis["synthesis"]["pattern_gate"]["threshold"] == 4, synthesis + assert synthesis["synthesis"]["visibility"]["mode"] == "explicit", synthesis + assert "design_conflict" in synthesis["synthesis"]["visibility"]["reasons"], synthesis + assert synthesis["synthesis"]["conflicts"], synthesis + markdown = (skill_dir / "reports" / "reference-synthesis.md").read_text(encoding="utf-8") + assert "Curated World-Class Pattern Tracks" in markdown, markdown[:600] + assert "Borrow Now" in markdown, markdown[:900] + assert "Default Recommendation" in markdown, markdown[:1200] + assert "Pattern Gate" in markdown, markdown[:1500] + assert "Conflict Check" in markdown, markdown[:1500] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_registry_audit.py b/tests/verify_registry_audit.py new file mode 100644 index 0000000..7e769df --- /dev/null +++ b/tests/verify_registry_audit.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "registry_audit.py" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_registry" + tmp_root.mkdir(parents=True, exist_ok=True) + output_json = tmp_root / "registry_audit.json" + output_md = tmp_root / "registry_audit.md" + registry_dir = tmp_root / "registry" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--registry-dir", + str(registry_dir), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + package = payload["package"] + assert payload["ok"], payload + assert payload["schema_version"] == "2.0", payload + assert package["name"] == "yao-meta-skill", package + assert package["version"] == "1.1.0", package + assert package["license"] == "MIT", package + assert package["checksums"]["package_sha256"], package + assert package["source"]["ir_schema_version"] == "2.0.0", package + assert "distribution" in package, package + if (ROOT / "reports" / "package_verification.json").exists(): + assert package["distribution"]["archive_verified"], package + assert package["checksums"]["archive_sha256"], package + if (ROOT / "reports" / "install_simulation.json").exists(): + assert package["distribution"]["install_simulated"], package + assert package["artifacts"]["install_simulation"].endswith("reports/install_simulation.md"), package + if (ROOT / "reports" / "adoption_drift_report.json").exists(): + assert package["artifacts"]["adoption_drift"].endswith("reports/adoption_drift_report.md"), package + if (ROOT / "reports" / "review_waivers.json").exists(): + assert package["artifacts"]["review_waivers"].endswith("reports/review_waivers.md"), package + if (ROOT / "reports" / "compiled_targets.json").exists(): + assert package["artifacts"]["compiled_targets"].endswith("reports/compiled_targets.md"), package + assert package["compatibility"]["openai"] == "pass", package + assert package["compatibility"]["claude"] == "pass", package + assert package["compatibility"]["generic"] == "pass", package + assert package["compatibility"]["vscode"] == "pass", package + assert (registry_dir / "index.json").exists(), registry_dir + assert (registry_dir / "packages" / "yao-meta-skill.json").exists(), registry_dir + assert output_json.exists(), output_json + assert output_md.exists(), output_md + assert "Registry Audit" in output_md.read_text(encoding="utf-8") + + broken = tmp_root / "broken-registry-skill" + (broken / "reports").mkdir(parents=True, exist_ok=True) + (broken / "agents").mkdir(parents=True, exist_ok=True) + (broken / "SKILL.md").write_text( + "---\nname: broken-registry-skill\ndescription: Broken registry fixture.\n---\n\n# Broken\n", + encoding="utf-8", + ) + (broken / "manifest.json").write_text(json.dumps({"name": "broken-registry-skill"}, indent=2), encoding="utf-8") + (broken / "agents" / "interface.yaml").write_text( + "interface:\n display_name: Broken\n short_description: Broken\n default_prompt: Broken\ncompatibility:\n adapter_targets: [generic]\n trust:\n source_tier: local\n", + encoding="utf-8", + ) + broken_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(broken), + "--registry-dir", + str(tmp_root / "broken-registry"), + "--output-json", + str(tmp_root / "broken.json"), + "--output-md", + str(tmp_root / "broken.md"), + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + assert broken_proc.returncode == 2, broken_proc.stdout + broken_payload = json.loads(broken_proc.stdout) + assert not broken_payload["ok"], broken_payload + assert any("version" in item for item in broken_payload["failures"]), broken_payload + assert any("checksum" in item for item in broken_payload["failures"]), broken_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_review_annotations.py b/tests/verify_review_annotations.py new file mode 100644 index 0000000..f3b48fa --- /dev/null +++ b/tests/verify_review_annotations.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_review_annotations.py" +STUDIO = ROOT / "scripts" / "render_review_studio.py" + + +def run(*args: str, check: bool = True) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, str(SCRIPT), *args], + cwd=ROOT, + capture_output=True, + text=True, + check=check, + ) + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_review_annotations" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + + empty_json = tmp_root / "empty.json" + empty_md = tmp_root / "empty.md" + empty_proc = run(str(ROOT), "--output-json", str(empty_json), "--output-md", str(empty_md)) + empty_payload = json.loads(empty_proc.stdout) + assert empty_payload["ok"], empty_payload + assert empty_payload["summary"]["annotation_count"] == 0, empty_payload + assert empty_payload["summary"]["open_blocker_count"] == 0, empty_payload + assert "No reviewer annotations recorded yet." in empty_md.read_text(encoding="utf-8"), empty_md + + source_json = tmp_root / "review_annotations_input.json" + template_proc = run( + str(ROOT), + "--annotations-json", + str(source_json), + "--output-json", + str(tmp_root / "template.json"), + "--output-md", + str(tmp_root / "template.md"), + "--write-template", + ) + template_payload = json.loads(template_proc.stdout) + assert template_payload["ok"], template_payload + assert template_payload["template_written"], template_payload + assert source_json.exists(), source_json + + warning_json = tmp_root / "warning.json" + warning_md = tmp_root / "warning.md" + add_proc = run( + str(ROOT), + "--annotations-json", + str(source_json), + "--output-json", + str(warning_json), + "--output-md", + str(warning_md), + "--add-annotation", + "--annotation-id", + "ann-output-1", + "--gate-key", + "output-lab", + "--target-path", + "reports/output_quality_scorecard.md", + "--line", + "1", + "--severity", + "warning", + "--reviewer", + "Yao QA", + "--created-at", + "2026-06-13", + "--body", + "Clarify whether the output evidence is recorded-fixture or model-executed.", + "--suggested-action", + "Open reports/output_execution_runs.md before release wording.", + ) + warning_payload = json.loads(add_proc.stdout) + assert warning_payload["ok"], warning_payload + assert warning_payload["summary"]["annotation_count"] == 1, warning_payload + assert warning_payload["summary"]["open_warning_count"] == 1, warning_payload + annotation = warning_payload["annotations"][0] + assert annotation["target_exists"], annotation + assert annotation["source_excerpt"], annotation + assert "ann-output-1" in warning_md.read_text(encoding="utf-8"), warning_md + + blocker_source = tmp_root / "blocker_input.json" + blocker_source.write_text( + json.dumps( + { + "schema_version": "1.0", + "annotations": [ + { + "id": "ann-release-blocker", + "gate_key": "release-notes", + "target_path": "reports/skill-os-2-review.md", + "line": 1, + "severity": "blocker", + "status": "open", + "reviewer": "Yao QA", + "created_at": "2026-06-13", + "body": "Release narrative needs one reviewer pass before publish.", + } + ], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + blocker_json = ROOT / "reports" / "review_annotations.json" + blocker_md = ROOT / "reports" / "review_annotations.md" + original_json = blocker_json.read_text(encoding="utf-8") if blocker_json.exists() else None + original_md = blocker_md.read_text(encoding="utf-8") if blocker_md.exists() else None + try: + blocker_proc = run( + str(ROOT), + "--annotations-json", + str(blocker_source), + "--output-json", + str(blocker_json), + "--output-md", + str(blocker_md), + ) + blocker_payload = json.loads(blocker_proc.stdout) + assert blocker_payload["ok"], blocker_payload + assert blocker_payload["summary"]["open_blocker_count"] == 1, blocker_payload + + studio_json = tmp_root / "review-studio-blocked.json" + studio_html = tmp_root / "review-studio-blocked.html" + studio_proc = subprocess.run( + [ + sys.executable, + str(STUDIO), + str(ROOT), + "--output-html", + str(studio_html), + "--output-json", + str(studio_json), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + studio_payload = json.loads(studio_proc.stdout) + assert studio_payload["summary"]["decision"] == "blocked", studio_payload + assert studio_payload["summary"]["open_annotation_blocker_count"] == 1, studio_payload + html = studio_html.read_text(encoding="utf-8") + assert "审查批注" in html, html[:4000] + assert "ann-release-blocker" in html, html + finally: + if original_json is None: + blocker_json.unlink(missing_ok=True) + else: + blocker_json.write_text(original_json, encoding="utf-8") + if original_md is None: + blocker_md.unlink(missing_ok=True) + else: + blocker_md.write_text(original_md, encoding="utf-8") + + invalid_proc = run( + str(ROOT), + "--annotations-json", + str(tmp_root / "invalid_input.json"), + "--output-json", + str(tmp_root / "invalid.json"), + "--output-md", + str(tmp_root / "invalid.md"), + "--add-annotation", + "--gate-key", + "output-lab", + "--target-path", + "../outside.md", + "--body", + "This path must be rejected.", + check=False, + ) + assert invalid_proc.returncode == 2, invalid_proc.stdout + invalid_payload = json.loads(invalid_proc.stdout) + assert not invalid_payload["ok"], invalid_payload + assert "escapes skill directory" in "\n".join(invalid_payload["failures"]), invalid_payload + assert not (tmp_root / "invalid_input.json").exists(), invalid_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_review_studio.py b/tests/verify_review_studio.py new file mode 100644 index 0000000..1cacb83 --- /dev/null +++ b/tests/verify_review_studio.py @@ -0,0 +1,557 @@ +#!/usr/bin/env python3 +import json +import re +import sys + + +from review_studio_helpers import ROOT, prepare_tmp_root, render_review_studio_fixture +from review_studio_world_class_assertions import assert_world_class_action + +sys.path.insert(0, str(ROOT / "scripts")) +import render_review_studio as review_studio # noqa: E402 +import review_studio_formatting as review_formatting # noqa: E402 +import review_studio_gates as review_gates # noqa: E402 +import review_studio_layout as review_layout # noqa: E402 + + +def main() -> None: + tmp_root = prepare_tmp_root() + output_html, output_json, proc = render_review_studio_fixture(tmp_root) + payload = json.loads(proc.stdout) + assert payload["ok"], payload + assert payload["schema_version"] == "2.0", payload + assert payload["summary"]["decision"] == "review", payload + assert payload["summary"]["gate_count"] == 16, payload + assert payload["summary"]["gate_contract_ok"] is True, payload + assert payload["summary"]["world_class_score"] == 91, payload + assert payload["summary"]["warning_count"] == 3, payload + assert payload["summary"]["blocker_count"] == 0, payload + assert payload["summary"]["action_count"] == 3, payload + assert payload["summary"]["annotation_count"] == 0, payload + assert payload["summary"]["open_annotation_blocker_count"] == 0, payload + assert payload["summary"]["action_count"] == payload["summary"]["warning_count"] + payload["summary"]["blocker_count"], payload + assert {item["gate_key"] for item in payload["review_actions"]} == { + "output-lab", + "review-waivers", + "world-class-evidence", + }, payload + action_by_gate = {item["gate_key"]: item for item in payload["review_actions"]} + gate_keys = {item["key"] for item in payload["gates"]} + assert gate_keys == review_gates.REVIEW_STUDIO_GATE_KEYS, payload + for gate in payload["gates"]: + if gate["status"] == "pass": + assert gate["review_action_id"] == "", gate + assert gate["review_action_title"] == "", gate + assert gate["review_action_next_step"] == "", gate + assert gate["review_action_reason"] == "", gate + assert gate["review_action_source_ref_count"] == 0, gate + assert gate["review_action_verification_command"] == "", gate + continue + action = action_by_gate[gate["key"]] + assert gate["review_action_id"] == action["action_id"] == f"review-action-{gate['key']}", gate + assert gate["review_action_status"] == action["status"] == gate["status"], gate + assert gate["review_action_title"] == action["title"] == action["label"], gate + assert gate["review_action_next_step"] == action["next_step"] == action["summary"], gate + assert gate["review_action_reason"] == action["reason"] == action["why"], gate + assert gate["review_action_source_ref_count"] == len(action["source_refs"]) > 0, gate + assert gate["review_action_verification_command"] == action["verification_command"], gate + assert set(review_gates.GATE_WEIGHTS) == review_gates.REVIEW_STUDIO_GATE_KEYS, review_gates.GATE_WEIGHTS + gate_contract = payload["gate_contract"] + assert gate_contract["ok"] is True, gate_contract + assert gate_contract["expected_gate_count"] == 16, gate_contract + assert gate_contract["actual_gate_count"] == 16, gate_contract + assert set(gate_contract["expected_gate_keys"]) == review_gates.REVIEW_STUDIO_GATE_KEYS, gate_contract + assert set(gate_contract["rendered_gate_keys"]) == review_gates.REVIEW_STUDIO_GATE_KEYS, gate_contract + assert gate_contract["missing_gate_keys"] == [], gate_contract + assert gate_contract["unknown_gate_keys"] == [], gate_contract + assert gate_contract["duplicate_gate_keys"] == [], gate_contract + assert gate_contract["unweighted_gate_keys"] == [], gate_contract + output_gate = next(item for item in payload["gates"] if item["key"] == "output-lab") + assert output_gate["status"] == "warn", output_gate + assert "5/5 cases" in output_gate["detail"], output_gate + assert "file-backed 1" in output_gate["detail"], output_gate + assert "blind A/B 5" in output_gate["detail"], output_gate + assert "exec 10" in output_gate["detail"], output_gate + assert "model 10" in output_gate["detail"], output_gate + assert "reviewed 0/5" in output_gate["detail"], output_gate + assert "review pending 5" in output_gate["detail"], output_gate + context_gate = next(item for item in payload["gates"] if item["key"] == "context-budget") + assert context_gate["status"] == "pass", context_gate + initial_load = re.search(r"initial load (\d+)/1000", context_gate["detail"]) + assert initial_load, context_gate + assert int(initial_load.group(1)) <= 1000, context_gate + assert "deferred " in context_gate["detail"], context_gate + assert "/120000" in context_gate["detail"], context_gate + assert "top deferred scripts" in context_gate["detail"], context_gate + assert "resource governance governed" in context_gate["detail"], context_gate + assert "quality density" in context_gate["detail"], context_gate + release_gate = next(item for item in payload["gates"] if item["key"] == "release-notes") + assert "upgrade minor declared / minor recommended" in release_gate["detail"], release_gate + assert "reports/upgrade_check.json" in release_gate["evidence"], release_gate + registry_gate = next(item for item in payload["gates"] if item["key"] == "registry-audit") + assert "install pass" in registry_gate["detail"], registry_gate + assert "installer permissions 12 enforced / 0 failures" in registry_gate["detail"], registry_gate + assert "reports/install_simulation.json" in registry_gate["evidence"], registry_gate + trust_gate = next(item for item in payload["gates"] if item["key"] == "trust-report") + assert trust_gate["status"] == "pass", trust_gate + assert "3 network-capable scripts" in trust_gate["detail"], trust_gate + assert "0 help smoke failures" in trust_gate["detail"], trust_gate + python_compat_gate = next(item for item in payload["gates"] if item["key"] == "python-compat") + assert python_compat_gate["status"] == "pass", python_compat_gate + assert "Python 3.11" in python_compat_gate["detail"], python_compat_gate + assert "0 compatibility issues" in python_compat_gate["detail"], python_compat_gate + assert "0 f-string 3.11 hazards" in python_compat_gate["detail"], python_compat_gate + assert python_compat_gate["evidence"] == "reports/python_compatibility.json", python_compat_gate + architecture_gate = next(item for item in payload["gates"] if item["key"] == "architecture-maintainability") + assert architecture_gate["status"] == "pass", architecture_gate + assert "0 hotspots" in architecture_gate["detail"], architecture_gate + assert re.search(r"\d+ watchlist files?", architecture_gate["detail"]), architecture_gate + assert re.search(r"\d+ early watch files?", architecture_gate["detail"]), architecture_gate + assert "0 blockers" in architecture_gate["detail"], architecture_gate + assert "CLI handlers" in architecture_gate["detail"], architecture_gate + assert architecture_gate["evidence"] == "reports/architecture_maintainability.json", architecture_gate + permission_gate = next(item for item in payload["gates"] if item["key"] == "permission-gates") + assert permission_gate["status"] == "pass", permission_gate + assert "permissions approved" in permission_gate["detail"], permission_gate + assert "gaps 0" in permission_gate["detail"], permission_gate + permission_runtime_gate = next(item for item in payload["gates"] if item["key"] == "permission-runtime") + assert permission_runtime_gate["status"] == "pass", permission_runtime_gate + assert "metadata fallback 4" in permission_runtime_gate["detail"], permission_runtime_gate + assert "native 0" in permission_runtime_gate["detail"], permission_runtime_gate + intent_gate = next(item for item in payload["gates"] if item["key"] == "intent-canvas") + assert intent_gate["status"] == "pass", intent_gate + assert "intent confidence 100/100" in intent_gate["detail"], intent_gate + atlas_gate = next(item for item in payload["gates"] if item["key"] == "skill-atlas") + assert atlas_gate["status"] == "pass", atlas_gate + assert "actionable route collisions" in atlas_gate["detail"], atlas_gate + assert "actionable drift" in atlas_gate["detail"], atlas_gate + operations_gate = next(item for item in payload["gates"] if item["key"] == "operations-loop") + assert operations_gate["status"] == "pass", operations_gate + assert "metadata events" in operations_gate["detail"], operations_gate + assert "risk low" in operations_gate["detail"], operations_gate + assert "daily proposals" in operations_gate["detail"], operations_gate + assert "weekly queue" in operations_gate["detail"], operations_gate + assert "reports/adoption_drift_report.json" in operations_gate["evidence"], operations_gate + assert "reports/skillops/daily" in operations_gate["evidence"], operations_gate + assert "reports/skillops/weekly" in operations_gate["evidence"], operations_gate + waivers_gate = next(item for item in payload["gates"] if item["key"] == "review-waivers") + assert waivers_gate["status"] == "warn", waivers_gate + assert "1 warning gates still need reviewer decision" in waivers_gate["detail"], waivers_gate + assert "reports/review_waivers.json" in waivers_gate["evidence"], waivers_gate + world_class_gate = next(item for item in payload["gates"] if item["key"] == "world-class-evidence") + world_class_summary = json.loads((ROOT / "reports" / "world_class_evidence_ledger.json").read_text(encoding="utf-8"))[ + "summary" + ] + assert world_class_gate["status"] == "warn", world_class_gate + assert "4 pending world-class evidence entries" in world_class_gate["detail"], world_class_gate + assert "1 human pending" in world_class_gate["detail"], world_class_gate + assert "3 external pending" in world_class_gate["detail"], world_class_gate + assert ( + f"source checks {world_class_summary['source_pass_count']}/{world_class_summary['source_check_count']} pass" + in world_class_gate["detail"] + ), world_class_gate + assert f"{world_class_summary['source_blocked_count']} blocked" in world_class_gate["detail"], world_class_gate + assert "overclaim guard true" in world_class_gate["detail"], world_class_gate + assert world_class_gate["evidence"] == "reports/world_class_evidence_ledger.json", world_class_gate + assert output_html.exists(), output_html + assert output_json.exists(), output_json + full_payload = json.loads(output_json.read_text(encoding="utf-8")) + assert full_payload["evidence_paths"]["skill_ir"] == "skill-ir/examples/yao-meta-skill.json", full_payload[ + "evidence_paths" + ] + assert full_payload["evidence_paths"]["compiled_targets"] == "reports/compiled_targets.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["output_execution"] == "reports/output_execution_runs.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["output_blind_review"] == "reports/output_blind_review_pack.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["output_review_kit_html"] == "reports/output_review_kit.html", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["output_review_decisions"] == "reports/output_review_decisions.json", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["output_review_adjudication"] == "reports/output_review_adjudication.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["python_compatibility"] == "reports/python_compatibility.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["architecture_maintainability"] == "reports/architecture_maintainability.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "benchmark_reproducibility.md").exists(): + assert full_payload["evidence_paths"]["benchmark_reproducibility"] == "reports/benchmark_reproducibility.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "skill_os2_coverage.md").exists(): + assert full_payload["evidence_paths"]["skill_os2_coverage"] == "reports/skill_os2_coverage.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["daily_skillops"].startswith("reports/skillops/daily/"), full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["weekly_curator"].startswith("reports/skillops/weekly/"), full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["review_annotations"] == "reports/review_annotations.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["adaptation_proposals"] == "reports/adaptation_proposals.md", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["adaptation_approval_ledger"] == "reports/adaptation_approval_ledger.json", full_payload["evidence_paths"] + assert full_payload["evidence_paths"]["adaptation_regression"] == "reports/adaptation_regression_report.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_evidence_plan.md").exists(): + assert full_payload["evidence_paths"]["world_class_evidence_plan"] == "reports/world_class_evidence_plan.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_evidence_ledger.md").exists(): + assert full_payload["evidence_paths"]["world_class_evidence_ledger"] == "reports/world_class_evidence_ledger.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_evidence_intake.md").exists(): + assert full_payload["evidence_paths"]["world_class_evidence_intake"] == "reports/world_class_evidence_intake.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_evidence_preflight.md").exists(): + assert full_payload["evidence_paths"]["world_class_evidence_preflight"] == "reports/world_class_evidence_preflight.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_evidence_preflight.html").exists(): + assert full_payload["evidence_paths"]["world_class_evidence_preflight_html"] == "reports/world_class_evidence_preflight.html", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_submission_review.md").exists(): + assert full_payload["evidence_paths"]["world_class_submission_review"] == "reports/world_class_submission_review.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_operator_runbook.md").exists(): + assert full_payload["evidence_paths"]["world_class_operator_runbook"] == "reports/world_class_operator_runbook.md", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_operator_runbook.html").exists(): + assert full_payload["evidence_paths"]["world_class_operator_runbook_html"] == "reports/world_class_operator_runbook.html", full_payload["evidence_paths"] + if (ROOT / "reports" / "world_class_claim_guard.md").exists(): + assert full_payload["evidence_paths"]["world_class_claim_guard"] == "reports/world_class_claim_guard.md", full_payload["evidence_paths"] + assert full_payload["data"]["output_blind_review"]["summary"]["pair_count"] == 5, full_payload["data"]["output_blind_review"] + assert full_payload["data"]["output_execution"]["summary"]["command_executed_count"] == 10, full_payload["data"]["output_execution"] + assert full_payload["data"]["output_execution"]["summary"]["recorded_fixture_count"] == 0, full_payload["data"]["output_execution"] + assert full_payload["data"]["output_execution"]["summary"]["model_executed_count"] == 10, full_payload["data"]["output_execution"] + assert full_payload["data"]["output_execution"]["summary"]["token_observed_count"] == 10, full_payload["data"]["output_execution"] + assert full_payload["data"]["output_execution"]["summary"]["token_estimated_count"] == 0, full_payload["data"]["output_execution"] + assert full_payload["data"]["output_review_adjudication"]["summary"]["pending_count"] == 5, full_payload["data"]["output_review_adjudication"] + assert full_payload["data"]["output_review_adjudication"]["summary"]["answer_revealed_count"] == 0, full_payload["data"]["output_review_adjudication"] + assert full_payload["data"]["output_review_adjudication"]["summary"]["pending_answer_hidden_count"] == 5, full_payload["data"]["output_review_adjudication"] + assert full_payload["data"]["output_review_adjudication"]["summary"]["reviewer_checklist_count"] == 5, full_payload["data"]["output_review_adjudication"] + assert full_payload["data"]["output_review_adjudication"]["summary"]["reviewer_checklist_pending_count"] == 5, full_payload["data"]["output_review_adjudication"] + assert full_payload["data"]["output_review_adjudication"]["summary"]["reviewer_checklist_ready_count"] == 0, full_payload["data"]["output_review_adjudication"] + assert all(not item["expected_revealed"] for item in full_payload["data"]["output_review_adjudication"]["pairs"]), full_payload["data"]["output_review_adjudication"] + benchmark_summary = full_payload["data"]["benchmark_reproducibility"]["summary"] + assert benchmark_summary["reproducibility_ready"] is True, benchmark_summary + assert benchmark_summary["release_lock_ready"] == (benchmark_summary["source_tree_dirty"] is False), benchmark_summary + assert "generated_tree_dirty" in benchmark_summary, benchmark_summary + expected_beta_ready = ( + benchmark_summary["reproducibility_ready"] + and benchmark_summary["release_lock_ready"] + and benchmark_summary["provider_evidence_complete"] + ) + assert benchmark_summary["beta_test_ready"] is expected_beta_ready, benchmark_summary + assert benchmark_summary["beta_test_deferred_evidence_count"] >= 1, benchmark_summary + beta_release = full_payload["data"]["benchmark_reproducibility"]["beta_test_release"] + assert beta_release["ready"] is expected_beta_ready, beta_release + assert not any("human blind-review" in item for item in beta_release["blockers"]), beta_release + pending_ledger_keys = { + item["key"] + for item in full_payload["data"]["world_class_evidence_ledger"]["entries"] + if item.get("status") == "pending" + } + deferred_keys = {item["key"] for item in beta_release["allowed_deferred_evidence"]} + assert deferred_keys == pending_ledger_keys, beta_release + assert any(item["key"] == "human-adjudication" for item in beta_release["allowed_deferred_evidence"]), ( + beta_release + ) + assert benchmark_summary["public_claim_ready"] is False, benchmark_summary + assert benchmark_summary["public_claim_blocker_count"] >= 3, benchmark_summary + public_claim = full_payload["data"]["benchmark_reproducibility"]["public_claim"] + assert public_claim["ready"] is False, public_claim + assert not any("provider-backed model holdout evidence is incomplete" in item for item in public_claim["blockers"]), public_claim + assert any("human blind-review adjudication is incomplete" in item for item in public_claim["blockers"]), public_claim + output_review_checklist = full_payload["data"]["output_review_adjudication"]["reviewer_checklist"] + assert len(output_review_checklist) == 5, output_review_checklist + assert all(not item["answer_key_visible"] for item in output_review_checklist), output_review_checklist + assert output_review_checklist[0]["commands"]["adjudicate"] == "python3 scripts/yao.py output-review", output_review_checklist[0] + assert full_payload["data"]["review_annotations"]["summary"]["annotation_count"] == 0, full_payload["data"]["review_annotations"] + daily_skillops_summary = full_payload["data"]["daily_skillops"]["summary"] + assert daily_skillops_summary["writes_source_files"] is False, daily_skillops_summary + assert daily_skillops_summary["auto_patch_enabled"] is False, daily_skillops_summary + weekly_curator_summary = full_payload["data"]["weekly_curator"]["summary"] + assert weekly_curator_summary["unique_opportunity_count"] >= 1, weekly_curator_summary + assert weekly_curator_summary["writes_source_files"] is False, weekly_curator_summary + assert weekly_curator_summary["auto_patch_enabled"] is False, weekly_curator_summary + assert full_payload["data"]["adaptation_proposals"]["report_contract"]["contract"] == "adaptation-proposals", full_payload["data"]["adaptation_proposals"] + assert full_payload["data"]["adaptation_proposals"]["proposal_count"] == full_payload["data"]["adaptation_proposals"]["summary"]["proposal_count"], full_payload["data"]["adaptation_proposals"] + assert full_payload["data"]["adaptation_approval_ledger"]["report_contract"]["contract"] == "adaptation-approval-ledger", full_payload["data"]["adaptation_approval_ledger"] + assert full_payload["data"]["adaptation_approval_ledger"]["pending_review_count"] == 0, full_payload["data"]["adaptation_approval_ledger"] + assert full_payload["data"]["adaptation_regression"]["report_contract"]["contract"] == "adaptation-regression-report", full_payload["data"]["adaptation_regression"] + assert full_payload["data"]["adaptation_regression"]["approval_draft_supported"] is True, full_payload["data"]["adaptation_regression"] + waiver_summary = full_payload["data"]["review_waivers"]["summary"] + assert waiver_summary["waiver_candidate_count"] == 2, waiver_summary + assert waiver_summary["waiverable_open_count"] == 1, waiver_summary + assert waiver_summary["non_waivable_count"] == 1, waiver_summary + waiver_candidates = {item["gate_key"]: item for item in full_payload["data"]["review_waivers"]["waiver_candidates"]} + assert waiver_candidates["output-lab"]["waiver_allowed"] is True, waiver_candidates + assert waiver_candidates["output-lab"]["status"] == "needs-reviewer-decision", waiver_candidates + assert waiver_candidates["output-lab"]["risk_summary"] == "review pending 5; model-executed 10; output failures 0", waiver_candidates + assert "review-waivers . --add-waiver" in waiver_candidates["output-lab"]["suggested_command"], waiver_candidates + assert "Does not count as provider, human, or public world-class completion evidence" in waiver_candidates["output-lab"]["world_class_boundary"], waiver_candidates + assert waiver_candidates["world-class-evidence"]["waiver_allowed"] is False, waiver_candidates + assert waiver_candidates["world-class-evidence"]["status"] == "cannot-waive", waiver_candidates + assert waiver_candidates["world-class-evidence"]["risk_summary"] == "4 pending evidence entries; 1 human pending; 3 external pending", waiver_candidates + assert "Non-waivable completion boundary" in waiver_candidates["world-class-evidence"]["world_class_boundary"], waiver_candidates + assert full_payload["data"]["compiled_targets"]["summary"]["target_count"] >= 4, full_payload["data"]["compiled_targets"] + assert full_payload["data"]["compiled_targets"]["summary"]["block_count"] == 0, full_payload["data"]["compiled_targets"] + assert full_payload["data"]["runtime_permissions"]["summary"]["metadata_fallback_count"] == 4, full_payload["data"]["runtime_permissions"] + assert full_payload["evidence_paths"]["runtime_permissions"] == "reports/runtime_permission_probes.md", full_payload["evidence_paths"] + assert full_payload["data"]["python_compatibility"]["summary"]["target_python"] == "3.11", full_payload["data"]["python_compatibility"] + assert full_payload["data"]["python_compatibility"]["summary"]["issue_count"] == 0, full_payload["data"]["python_compatibility"] + assert full_payload["data"]["architecture_maintainability"]["summary"]["hotspot_count"] == 0, full_payload["data"]["architecture_maintainability"] + assert full_payload["data"]["architecture_maintainability"]["summary"]["watchlist_count"] == 0, full_payload["data"]["architecture_maintainability"] + assert full_payload["data"]["architecture_maintainability"]["summary"]["early_watchlist_count"] >= 2, full_payload["data"]["architecture_maintainability"] + assert full_payload["data"]["architecture_maintainability"]["summary"]["blocker_count"] == 0, full_payload["data"]["architecture_maintainability"] + early_watch_paths = { + item["path"] for item in full_payload["data"]["architecture_maintainability"]["early_watchlist"] + } + assert "scripts/cross_packager.py" not in early_watch_paths, full_payload["data"]["architecture_maintainability"] + action_keys = {item["gate_key"] for item in full_payload["review_actions"]} + assert action_keys == {"output-lab", "review-waivers", "world-class-evidence"}, full_payload["review_actions"] + assert_world_class_action(full_payload) + if full_payload["data"]["skill_os2_coverage"]: + assert full_payload["data"]["skill_os2_coverage"]["summary"]["local_blueprint_ready"] is True, full_payload["data"]["skill_os2_coverage"] + assert full_payload["data"]["skill_os2_coverage"]["summary"]["public_world_class_ready"] is False, full_payload["data"]["skill_os2_coverage"] + assert full_payload["data"]["skill_os2_coverage"]["summary"]["extension_track_count"] == 4, full_payload["data"]["skill_os2_coverage"] + assert full_payload["data"]["skill_os2_coverage"]["summary"]["extension_partial_count"] == 0, full_payload["data"]["skill_os2_coverage"] + assert full_payload["data"]["skill_os2_coverage"]["summary"]["extension_planned_count"] == 0, full_payload["data"]["skill_os2_coverage"] + assert full_payload["data"]["skill_os2_coverage"]["summary"]["extension_covered_count"] == 4, full_payload["data"]["skill_os2_coverage"] + assert full_payload["data"]["skill_os2_coverage"]["summary"]["adaptive_extension_ready"] is True, full_payload["data"]["skill_os2_coverage"] + extension_tracks = { + item["key"]: item + for item in full_payload["data"]["skill_os2_coverage"]["reference_extension_tracks"] + } + assert extension_tracks["skill-interpretation-report"]["status"] == "covered", extension_tracks + assert extension_tracks["adaptive-self-iteration"]["status"] == "covered", extension_tracks + assert extension_tracks["daily-skillops-report"]["status"] == "covered", extension_tracks + assert extension_tracks["weekly-curator-report"]["status"] == "covered", extension_tracks + world_class_entries = full_payload["data"]["world_class_evidence_ledger"]["entries"] + assert len(world_class_entries) == 4, world_class_entries + assert {item["key"] for item in world_class_entries} == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, world_class_entries + provider_entry = next(item for item in world_class_entries if item["key"] == "provider-holdout") + assert provider_entry["status"] == "pending", provider_entry + assert "reports/output_execution_runs.json summary.model_executed_count > 0" in provider_entry["success_checks"], provider_entry + assert any("output-exec --provider-runner openai" in step for step in provider_entry["runbook"]), provider_entry + assert provider_entry["observed_state"]["model_executed_count"] == 10, provider_entry + assert provider_entry["observed_state"]["token_observed_count"] == 10, provider_entry + provider_submission_status = provider_entry["submission_state"]["status"] + assert provider_submission_status in {"invalid-contract", "missing"}, provider_entry + if provider_submission_status == "invalid-contract": + assert provider_entry["submission_state"]["artifact_sha256_verified_count"] == 1, provider_entry + else: + assert provider_entry["submission_state"].get("artifact_sha256_verified_count", 0) == 0, provider_entry + assert provider_entry["submission_state"]["ledger_counts_as_completion"] is False, provider_entry + assert full_payload["data"]["atlas"]["summary"]["actionable_route_collision_count"] == 0, full_payload["data"]["atlas"] + assert full_payload["data"]["atlas"]["summary"]["actionable_drift_signal_count"] == 0, full_payload["data"]["atlas"] + assert full_payload["data"]["atlas"]["summary"]["non_actionable_issue_count"] >= 1, full_payload["data"]["atlas"] + synthetic_actions = review_studio.build_review_actions( + [ + { + "key": "output-lab", + "label": "输出实验", + "status": "warn", + "detail": "synthetic missing output coverage", + "evidence": "reports/output_quality_scorecard.json", + "link": "reports/output_quality_scorecard.md", + } + ], + ROOT, + output_html, + ) + assert len(synthetic_actions) == 1, synthetic_actions + assert synthetic_actions[0]["title"] == "输出实验", synthetic_actions + assert synthetic_actions[0]["next_step"] == synthetic_actions[0]["summary"], synthetic_actions + assert synthetic_actions[0]["reason"] == synthetic_actions[0]["why"], synthetic_actions + assert synthetic_actions[0]["source_refs"], synthetic_actions + assert {item["path"] for item in synthetic_actions[0]["source_refs"]} >= { + "evals/output/cases.jsonl", + "reports/output_quality_scorecard.md", + "reports/output_execution_runs.md", + "reports/output_blind_review_pack.md", + "reports/output_review_kit.html", + "reports/output_review_adjudication.md", + }, synthetic_actions + assert all(item["exists"] for item in synthetic_actions[0]["source_refs"]), synthetic_actions + assert all(isinstance(item["line"], int) and item["line"] >= 1 for item in synthetic_actions[0]["source_refs"]), synthetic_actions + assert all("excerpt" in item for item in synthetic_actions[0]["source_refs"]), synthetic_actions + assert any(item["path"] == "reports/output_review_kit.html" and item["matched_pattern"] == "Output Review Kit" and "Output Review Kit" in item["excerpt"] for item in synthetic_actions[0]["source_refs"]), synthetic_actions + assert any(item["path"] == "evals/output/cases.jsonl" and item["matched_pattern"] == '"id"' and '"id"' in item["excerpt"] for item in synthetic_actions[0]["source_refs"]), synthetic_actions + synthetic_json = json.dumps(synthetic_actions, ensure_ascii=False) + assert str(ROOT) not in synthetic_json, synthetic_json + synthetic_html = review_studio.render_review_actions(synthetic_actions) + assert "source-ref-list" in synthetic_html, synthetic_html + assert "evals/output/cases.jsonl" in synthetic_html, synthetic_html + assert "
    " in synthetic_html, synthetic_html + assert "pattern: Output Review Kit" in synthetic_html, synthetic_html + html = output_html.read_text(encoding="utf-8") + assert "Review Studio 2.0" in html, html[:400] + assert "
    " in html, html + assert "pattern: # World-Class Evidence Ledger" in html, html + assert "审查闸门" in html, html[:1200] + assert "修复动作" in html, html[:3000] + assert "补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。" in html, html[:9000] + assert "resource governance governed" in html, html[:9000] + assert "reports/output_review_kit.html" in html, html[:9000] + assert "python3 scripts/adjudicate_output_review.py --write-template" in html, html[:9000] + assert "对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。" in html, html[:9000] + assert "补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据" in html, html + assert "action-card warn with-evidence" in html, html + assert "action-evidence-panel" in html, html + assert "证据采集" in html, html + assert "action-evidence-grid" in html, html + assert "action-evidence-checks" in html, html + assert "阶段队列" in html, html + assert "action-phase-list" in html, html + assert "action-phase-row blocked" in html, html + assert "#20 · unblock-access" in html, html + assert "#40 · collect-source" in html, html + assert "action-repair-list" in html, html + assert "action-repair-row blocked" in html, html + assert "provider-api-key" in html, html + assert "model_executed_count" in html, html + assert "#20 · unblock-access · precheck" in html, html + assert "#40 · collect-source · source-check" in html, html + assert "operator with provider credentials" in html, html + assert "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions" in html, html + assert "action-command-list" in html, html + assert "action-runbook-list" in html, html + assert "采集契约" in html, html + assert "action-collection-grid" in html, html + assert "来源要求" in html, html + assert "通过条件" in html, html + assert "证据资产" in html, html + assert "隐私边界" in html, html + assert "世界证据" in html, html + assert "证据台账" in html, html + assert "证据入口" in html, html + assert "入口边界" in html, html + assert "声明守卫" in html, html + assert "声明边界" in html, html + assert "提交清单" in html, html + assert "world-intake-grid" in html, html + assert "操作命令" in html, html + assert "收集要求" in html, html + assert "通过条件" in html, html + assert "evidence/world_class/submissions/provider-holdout.json" in html, html + assert "evidence/world_class/templates/provider-holdout.intake.json" in html, html + assert "Native Client Telemetry" in html, html + assert "native-client-telemetry.json" in html, html + assert "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" in html, html + assert "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions" in html, html + assert "资产角色" in html, html + assert "submission-ref" in html, html + assert "supporting-evidence" in html, html + assert "artifact_refs: true" in html, html + assert "intake 只校验证据包格式、来源、隐私和反过度声明" in html, html + assert "reports/world_class_evidence_intake.md" in html, html + assert "reports/world_class_operator_runbook.html" in html, html + assert "reports/world_class_claim_guard.md" in html, html + assert "日常运维" in html, html + assert "周度队列" in html, html + assert "Weekly Queue" in html, html + assert "英文完成断言、true 状态声明或中文完成态" in html, html + assert "world-evidence-grid" in html, html + assert "Provider Holdout" in html, html + assert "Native Permission Enforcement" in html, html + assert "提交态" in html, html + assert "status: missing" in html, html + assert "完成定义" in html, html + assert "证据来源" in html, html + assert "隐私约束" in html, html + assert "reports/output_execution_runs.json summary.model_executed_count > 0" in html, html + assert "计划、metadata fallback、待评审和本地命令不会被当成完成证据" in html, html + assert "执行步骤" in html, html + assert "output-exec --provider-runner openai" in html, html + assert "<redacted>" not in html and "" not in html, html + assert "world-runbook-panel" in html, html + assert "源证据检查" in html, html + assert "world-source-checks" in html, html + assert "Provider model run" in html, html + assert "model_executed_count: 10 / >0" in html, html + assert "Token usage observed" in html, html + assert "蓝图覆盖" in html, html + assert "Extension Track Count" in html, html + assert "Adaptive Extension Ready" in html, html + assert "
    Extension Partial Count
    0
    " in html, html + assert "
    Extension Covered Count
    4
    " in html, html + assert "
    Extension Planned Count
    0
    " in html, html + assert "Adaptive Extension Ready" in html, html + assert "本地蓝图" in html, html + assert "public world-class 仍以 world-class evidence ledger" in html, html + assert "公开声明" in html, html + assert "声明阻断" in html, html + assert "可公开声明" in html, html + assert "provider-backed model holdout evidence is incomplete" not in html, html + assert "human blind-review adjudication is incomplete" in html, html + assert "审查批注" in html, html[:9000] + assert "当前没有 reviewer 批注" in html, html[:9000] + assert "输出实验" in html, html[:2000] + assert "执行证据" in html, html + assert "盲评包" in html, html[:5000] + assert "审定报告" in html, html + assert "评审清单" in html, html + assert "output-review-grid" in html, html + assert "awaiting-decision" in html, html + assert "答案隐藏" in html, html + assert "winner_variant" in html, html + assert "python3 scripts/yao.py output-review" in html, html + assert "答案揭示" in html, html + assert "答案隐藏" in html, html + assert "注册审计" in html, html[:3000] + assert "包体验证" in html, html[:5000] + assert "Upgrade" in html, html[:6000] + assert "Compiler" in html, html[:6000] + assert "目标编译" in html, html[:9000] + assert "reports/compiled_targets.md" in output_json.read_text(encoding="utf-8"), output_json + assert "Install" in html, html[:6500] + assert "运营回路" in html, html[:7600] + assert "人工批准" in html, html[:8200] + assert "warning 可以被有边界地接受" in html, html + assert "批准概况" in html, html + assert "批准候选" in html, html + assert "waiver-candidate-grid" in html, html + assert "可批准 · needs-reviewer-decision" in html, html + assert "不可批准 · cannot-waive" in html, html + assert "review pending 5; model-executed 10; output failures 0" in html, html + assert "4 pending evidence entries; 1 human pending; 3 external pending" in html, html + assert "Does not count as provider, human, or public world-class completion evidence" in html, html + assert "Non-waivable completion boundary" in html, html + assert "python3 scripts/yao.py review-waivers . --add-waiver" in html, html + assert "Reviewer confirms this release does not claim provider-backed or human-adjudicated output superiority" in html, html + assert "Do not use a waiver to claim public world-class readiness" in html, html + assert "权限批准" in html, html[:9000] + assert "权限探针" in html, html[:9500] + assert "Python 兼容" in html, html[:10000] + assert "解释器边界" in html, html[:10000] + assert "reports/python_compatibility.md" in html, html + assert "架构维护" in html, html + assert "Arch Debt" in html, html + assert "reports/architecture_maintainability.md" in html, html + assert "0 hotspots" in html, html + assert re.search(r"\d+ watchlist files?", html), html + assert "kv-grid" in html, html + assert "案例数" in html, html + assert "命令执行" in html, html + assert "包体哈希" in html, html + assert "{'" not in html, html + assert "'case_count'" not in html, html + assert "'name'" not in html, html + assert "reports/review_waivers.md" in output_json.read_text(encoding="utf-8"), output_json + assert "upgrade minor declared / minor recommended" in html, html[:8000] + assert str(ROOT) not in output_json.read_text(encoding="utf-8"), output_json + formatted = review_formatting.render_kv_grid( + {"case_count": 5, "package_sha256": "abc123"}, + ["case_count", "package_sha256"], + "missing", + ) + assert "kv-grid" in formatted, formatted + assert "案例数" in formatted, formatted + assert "abc123" in formatted, formatted + assert review_formatting.value_text({"case_count": 5}) == "案例数: 5" + assert review_gates.min_output_cases("scaffold") == 1 + assert review_gates.min_output_cases("production") == 3 + assert review_gates.min_output_cases("governed") == 5 + assert review_gates.status_label("warn") == "关注" + assert review_gates.weighted_score([{"key": "output-lab", "status": "pass"}]) == 100 + assert review_gates.weighted_score([{"key": "output-lab", "status": "warn"}]) == 60 + assert not review_gates.gate_contract([{"key": "output-lab", "status": "pass"}])["ok"] + assert len(review_layout.REVIEW_STUDIO_NAV) == 16, review_layout.REVIEW_STUDIO_NAV + assert ("#world-class", "世界证据") in review_layout.REVIEW_STUDIO_NAV, review_layout.REVIEW_STUDIO_NAV + assert "position: sticky" in review_layout.review_studio_css(), review_layout.review_studio_css()[:400] + assert review_layout.review_studio_css() == (ROOT / "assets" / "review-studio.css").read_text( + encoding="utf-8" + ).strip() + assert "#overview" in review_layout.render_review_nav(), review_layout.render_review_nav() + assert "审查总览" in review_layout.render_review_nav(), review_layout.render_review_nav() + assert "闸门契约" in html, html[:9000] + assert "期望 Gate" in html, html[:9000] + assert "实际 Gate" in html, html[:9000] + assert review_layout.render_review_nav([]) == "" + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_review_viewer.py b/tests/verify_review_viewer.py new file mode 100644 index 0000000..3d603f6 --- /dev/null +++ b/tests/verify_review_viewer.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def assert_root_world_class_commands_are_current() -> None: + root_json = ROOT / "reports" / "review-viewer.json" + root_html = ROOT / "reports" / "review-viewer.html" + agent_docs = (ROOT / "AGENTS.md").read_text(encoding="utf-8") + assert root_json.exists(), root_json + assert root_html.exists(), root_html + json_text = root_json.read_text(encoding="utf-8") + assert 'python3 scripts/yao.py world-class-intake ."' not in json_text, root_json + assert "world-class-intake . --submissions-dir evidence/world_class/submissions" in json_text, root_json + for fragment in ( + 'python3 scripts/render_skill_os2_coverage.py . --generated-at "$GENERATED_AT"', + 'python3 scripts/render_benchmark_reproducibility.py . --generated-at "$GENERATED_AT"', + "python3 scripts/render_review_viewer.py .", + "release_lock_ready: false", + ): + assert fragment in agent_docs, fragment + + +def main() -> None: + assert_root_world_class_commands_are_current() + tmp_root = ROOT / "tests" / "tmp_review_viewer" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "review-viewer-demo", + "--description", + "Turn rough requests into a reusable review package.", + "--output-dir", + str(tmp_root), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = tmp_root / "review-viewer-demo" + proc = subprocess.run( + [sys.executable, str(ROOT / "scripts" / "render_review_viewer.py"), str(skill_dir)], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + html_path = Path(payload["artifacts"]["html"]) + json_path = Path(payload["artifacts"]["json"]) + assert html_path.exists(), html_path + assert json_path.exists(), json_path + html_text = html_path.read_text(encoding="utf-8") + css_contract = (ROOT / "assets" / "review-viewer.css").read_text(encoding="utf-8").strip() + assert css_contract in html_text, html_text[:500] + assert 'rel="stylesheet"' not in html_text, html_text[:500] + assert "Architecture at a glance" in html_text, html_text[:500] + assert "Compare view" in html_text, html_text[:500] + assert "Variant diff studio" in html_text, html_text[:900] + assert "Evidence readiness" in html_text, html_text[:1200] + assert "Output risk profile" in html_text, html_text[:1600] + assert "Artifact design profile" in html_text, html_text[:2200] + assert "Visual quality gates" in html_text, html_text[:2400] + assert "Prompt quality profile" in html_text, html_text[:2600] + assert "RTF to skill mapping" in html_text, html_text[:2800] + assert "Reference coach" in html_text, html_text[:900] + assert "Reference synthesis" in html_text, html_text[:1200] + assert "Top three next moves" in html_text, html_text[:500] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_review_waivers.py b/tests/verify_review_waivers.py new file mode 100644 index 0000000..b7050a7 --- /dev/null +++ b/tests/verify_review_waivers.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_review_waivers.py" +sys.path.insert(0, str(ROOT / "scripts")) + +import render_review_waivers as review_waivers # noqa: E402 +import review_studio_gates as review_gates # noqa: E402 + + +def run(*args: str, check: bool = True) -> subprocess.CompletedProcess: + return subprocess.run( + [sys.executable, str(SCRIPT), *args], + cwd=ROOT, + capture_output=True, + text=True, + check=check, + ) + + +def main() -> None: + expected_gate_keys = { + "intent-canvas", + "trigger-lab", + "output-lab", + "context-budget", + "runtime-matrix", + "trust-report", + "python-compat", + "architecture-maintainability", + "permission-gates", + "permission-runtime", + "skill-atlas", + "operations-loop", + "review-waivers", + "world-class-evidence", + "registry-audit", + "release-notes", + } + assert review_gates.REVIEW_STUDIO_GATE_KEYS == expected_gate_keys, review_gates.REVIEW_STUDIO_GATE_KEYS + assert set(review_gates.GATE_WEIGHTS) == expected_gate_keys, review_gates.GATE_WEIGHTS + assert review_waivers.REVIEW_STUDIO_GATE_KEYS == review_gates.REVIEW_STUDIO_GATE_KEYS + assert review_waivers.NON_WAIVABLE_GATE_KEYS == {"review-waivers", "world-class-evidence"} + assert "python-compat" in review_waivers.WAIVERABLE_GATE_KEYS + assert "architecture-maintainability" in review_waivers.WAIVERABLE_GATE_KEYS + assert "world-class-evidence" not in review_waivers.WAIVERABLE_GATE_KEYS + + tmp_root = ROOT / "tests" / "tmp_review_waivers" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + skill = tmp_root / "waiver-demo" + (skill / "reports").mkdir(parents=True, exist_ok=True) + (skill / "SKILL.md").write_text("---\nname: waiver-demo\ndescription: Waiver demo.\n---\n\n# Demo\n", encoding="utf-8") + + empty = run(str(skill), "--generated-at", "2026-06-13") + empty_payload = json.loads(empty.stdout) + assert empty_payload["ok"], empty_payload + assert empty_payload["summary"]["waiver_count"] == 0, empty_payload + assert empty_payload["summary"]["waiver_candidate_count"] == 0, empty_payload + assert (skill / "reports" / "review_waivers.json").exists(), skill + assert (skill / "reports" / "review_waivers.md").exists(), skill + + added = run( + str(skill), + "--generated-at", + "2026-06-13", + "--add-waiver", + "--gate-key", + "trust-report", + "--reviewer", + "Yao Team", + "--reason", + "Network-capable scripts are reviewed and bounded for this release.", + "--expires-at", + "2026-09-30", + ) + added_payload = json.loads(added.stdout) + assert added_payload["ok"], added_payload + assert added_payload["summary"]["active_count"] == 1, added_payload + assert added_payload["summary"]["covered_gate_keys"] == ["trust-report"], added_payload + assert "trust-report" in (skill / "reports" / "review_waivers.md").read_text(encoding="utf-8") + + python_compat = run( + str(skill), + "--generated-at", + "2026-06-13", + "--add-waiver", + "--gate-key", + "python-compat", + "--reviewer", + "Yao Team", + "--reason", + "Python compatibility warning is understood and bounded to this release.", + "--expires-at", + "2026-09-30", + ) + python_compat_payload = json.loads(python_compat.stdout) + assert python_compat_payload["ok"], python_compat_payload + assert python_compat_payload["summary"]["covered_gate_keys"] == ["python-compat", "trust-report"], python_compat_payload + + architecture = run( + str(skill), + "--generated-at", + "2026-06-13", + "--add-waiver", + "--gate-key", + "architecture-maintainability", + "--reviewer", + "Yao Team", + "--reason", + "Architecture warning is accepted only while the planned split remains in scope.", + "--expires-at", + "2026-09-30", + ) + architecture_payload = json.loads(architecture.stdout) + assert architecture_payload["ok"], architecture_payload + assert architecture_payload["summary"]["covered_gate_keys"] == [ + "architecture-maintainability", + "python-compat", + "trust-report", + ], architecture_payload + + non_waivable = run( + str(skill), + "--generated-at", + "2026-06-13", + "--add-waiver", + "--gate-key", + "world-class-evidence", + "--reviewer", + "Yao Team", + "--reason", + "World class evidence cannot be waived even with a reviewer.", + "--expires-at", + "2026-09-30", + check=False, + ) + assert non_waivable.returncode == 2, non_waivable.stdout + assert "invalid choice" in non_waivable.stderr, non_waivable.stderr + + expired_source = tmp_root / "expired.json" + expired_source.write_text( + json.dumps( + { + "waivers": [ + { + "gate_key": "operations-loop", + "decision": "temporary-exception", + "reviewer": "Yao Team", + "reason": "No local telemetry sample exists yet, accepted only for historical demo review.", + "created_at": "2026-01-01", + "expires_at": "2026-01-31", + } + ] + }, + ensure_ascii=False, + indent=2, + ), + encoding="utf-8", + ) + expired = run(str(skill), "--waivers-json", str(expired_source), "--output-json", str(tmp_root / "expired_report.json"), "--output-md", str(tmp_root / "expired_report.md"), "--generated-at", "2026-06-13") + expired_payload = json.loads(expired.stdout) + assert expired_payload["ok"], expired_payload + assert expired_payload["summary"]["expired_count"] == 1, expired_payload + assert expired_payload["summary"]["active_count"] == 0, expired_payload + assert expired_payload["warnings"], expired_payload + + invalid_source = tmp_root / "invalid.json" + invalid_source.write_text( + json.dumps({"waivers": [{"gate_key": "trust-report", "reason": "too short"}]}, ensure_ascii=False), + encoding="utf-8", + ) + invalid = run(str(skill), "--waivers-json", str(invalid_source), "--output-json", str(tmp_root / "invalid_report.json"), "--output-md", str(tmp_root / "invalid_report.md"), "--generated-at", "2026-06-13", check=False) + invalid_payload = json.loads(invalid.stdout) + assert invalid.returncode == 2, invalid.stdout + assert not invalid_payload["ok"], invalid_payload + assert invalid_payload["summary"]["invalid_count"] == 1, invalid_payload + assert invalid_payload["failures"], invalid_payload + + root_report_json = tmp_root / "root_review_waivers.json" + root_report_md = tmp_root / "root_review_waivers.md" + root = run( + str(ROOT), + "--output-json", + str(root_report_json), + "--output-md", + str(root_report_md), + "--generated-at", + "2026-06-14", + ) + root_payload = json.loads(root.stdout) + assert root_payload["ok"], root_payload + assert root_payload["summary"]["waiver_candidate_count"] == 2, root_payload + assert root_payload["summary"]["waiverable_open_count"] == 1, root_payload + assert root_payload["summary"]["non_waivable_count"] == 1, root_payload + candidates = {item["gate_key"]: item for item in root_payload["waiver_candidates"]} + assert candidates["output-lab"]["waiver_allowed"] is True, candidates + assert candidates["output-lab"]["status"] == "needs-reviewer-decision", candidates + assert "review pending" in candidates["output-lab"]["risk_summary"], candidates + assert "review-waivers . --add-waiver" in candidates["output-lab"]["suggested_command"], candidates + assert candidates["world-class-evidence"]["waiver_allowed"] is False, candidates + assert candidates["world-class-evidence"]["status"] == "cannot-waive", candidates + assert "Non-waivable completion boundary" in candidates["world-class-evidence"]["world_class_boundary"], candidates + assert "--submissions-dir evidence/world_class/submissions" in candidates["world-class-evidence"]["suggested_command"], candidates + root_markdown = root_report_md.read_text(encoding="utf-8") + assert "Candidate Actions" in root_markdown, root_markdown + assert "World-class evidence completion cannot be waived" in root_markdown, root_markdown + assert "Review Studio gates" in root_markdown, root_markdown + assert "Waiverable gates" in root_markdown, root_markdown + assert "Non-waivable gates" in root_markdown, root_markdown + assert "`world-class-evidence` | `cannot-waive` | `false`" in root_markdown, root_markdown + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_route_confusion.py b/tests/verify_route_confusion.py new file mode 100644 index 0000000..fd13cc1 --- /dev/null +++ b/tests/verify_route_confusion.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + + +def main() -> None: + proc = subprocess.run( + [sys.executable, "scripts/build_confusion_matrix.py"], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + summary = payload["summary"] + ok = ( + proc.returncode == 0 + and summary["accuracy"] == 1.0 + and summary["misroute_count"] == 0 + and summary["ambiguous_case_count"] == 0 + and payload["route_stats"]["no_route"]["recall"] == 1.0 + ) + report = { + "ok": ok, + "summary": summary, + "route_stats": payload["route_stats"], + } + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not ok: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_runtime_permission_probes.py b/tests/verify_runtime_permission_probes.py new file mode 100644 index 0000000..da4615f --- /dev/null +++ b/tests/verify_runtime_permission_probes.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_runtime_permission" +SCRIPT = ROOT / "scripts" / "probe_runtime_permissions.py" +SIMULATOR = ROOT / "scripts" / "simulate_install.py" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stderr": proc.stderr, + } + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + package_dir = TMP / "dist" + + subprocess.run([sys.executable, str(ROOT / "scripts" / "trust_check.py"), str(ROOT)], cwd=ROOT, check=True, capture_output=True, text=True) + package = run( + [ + sys.executable, + str(ROOT / "scripts" / "cross_packager.py"), + str(ROOT), + "--platform", + "openai", + "--platform", + "claude", + "--platform", + "generic", + "--platform", + "vscode", + "--expectations", + str(ROOT / "evals" / "packaging_expectations.json"), + "--output-dir", + str(package_dir), + "--zip", + ] + ) + assert package["ok"], package + install = run( + [ + sys.executable, + str(SIMULATOR), + str(ROOT), + "--package-dir", + str(package_dir), + "--install-root", + str(TMP / "install-root"), + "--output-json", + str(TMP / "install_simulation.json"), + "--output-md", + str(TMP / "install_simulation.md"), + "--generated-at", + "2026-06-13", + ] + ) + assert install["ok"], install + + probe = run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--package-dir", + str(package_dir), + "--output-json", + str(TMP / "runtime_permission_probes.json"), + "--output-md", + str(TMP / "runtime_permission_probes.md"), + "--install-simulation-json", + str(TMP / "install_simulation.json"), + ] + ) + assert probe["ok"], probe + payload = probe["payload"] + summary = payload["summary"] + assert summary["target_count"] == 4, summary + assert summary["pass_count"] == 4, summary + assert summary["fail_count"] == 0, summary + assert summary["native_enforcement_count"] == 0, summary + assert summary["metadata_fallback_count"] == 4, summary + assert summary["installer_enforcement_source_status"] == "present", summary + assert summary["installer_enforcement_pass_count"] == 4, summary + assert summary["installer_permission_enforced_count"] == 12, summary + assert summary["installer_permission_failure_count"] == 0, summary + assert summary["world_class_native_evidence_ready"] is False, summary + assert summary["installer_enforcement_ready"] is True, summary + assert summary["residual_risk_count"] == 4, summary + assert payload["expected_capabilities"] == ["file_write", "network", "subprocess"], payload + assert {item["assurance"] for item in payload["targets"]} == {"metadata-fallback-explicit"}, payload["targets"] + assert all(item["installer_enforcement"]["enforced"] is True for item in payload["targets"]), payload["targets"] + assert (TMP / "runtime_permission_probes.md").exists(), TMP + + bad_dir = TMP / "bad-dist" + shutil.copytree(package_dir, bad_dir) + openai_adapter = bad_dir / "targets" / "openai" / "adapter.json" + bad_payload = json.loads(openai_adapter.read_text(encoding="utf-8")) + bad_payload["target_permission_contract"].pop("operator_note", None) + openai_adapter.write_text(json.dumps(bad_payload, ensure_ascii=False, indent=2), encoding="utf-8") + bad_probe = run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--package-dir", + str(bad_dir), + "--output-json", + str(TMP / "bad_runtime_permission_probes.json"), + "--output-md", + str(TMP / "bad_runtime_permission_probes.md"), + "--install-simulation-json", + str(TMP / "install_simulation.json"), + ] + ) + assert bad_probe["returncode"] == 2, bad_probe + assert not bad_probe["payload"]["ok"], bad_probe + assert any("operator_note" in item for item in bad_probe["payload"]["failures"]), bad_probe["payload"]["failures"] + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_security_boundaries.py b/tests/verify_security_boundaries.py new file mode 100644 index 0000000..172cc14 --- /dev/null +++ b/tests/verify_security_boundaries.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +import zipfile +from pathlib import Path + +import yaml + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_security" +CROSS_PACKAGER = ROOT / "scripts" / "cross_packager.py" +INIT_SKILL = ROOT / "scripts" / "init_skill.py" +CHECK_UPDATE = ROOT / "scripts" / "check_update.py" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = {} + if proc.stdout.strip(): + payload = json.loads(proc.stdout) + return { + "returncode": proc.returncode, + "ok": proc.returncode == 0, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def write_skill(root: Path, *, interface_extra: str = "") -> Path: + skill = root / "secure-demo" + (skill / "agents").mkdir(parents=True, exist_ok=True) + (skill / "SKILL.md").write_text( + "---\nname: secure-demo\ndescription: Secure demo skill.\n---\n\n# Secure Demo\n", + encoding="utf-8", + ) + interface_text = f"""interface: + display_name: "Secure Demo" + short_description: "Safe package demo" + default_prompt: "Use this demo." +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "openai" + - "generic" + activation: + mode: "manual" + paths: [] + execution: + context: "inline" + shell: "bash" + trust: + source_tier: "local" + remote_inline_execution: "forbid" + remote_metadata_policy: "allow-metadata-only" + degradation: + openai: "metadata-adapter" + generic: "neutral-source" +{interface_extra}""" + (skill / "agents" / "interface.yaml").write_text(interface_text, encoding="utf-8") + return skill + + +def test_dangerous_output_dir() -> dict: + skill = write_skill(TMP / "dangerous-output") + result = run( + [ + sys.executable, + str(CROSS_PACKAGER), + str(skill), + "--platform", + "generic", + "--output-dir", + str(ROOT), + ] + ) + passed = result["returncode"] == 2 and "Refusing dangerous output directory" in result["stdout"] + return {"name": "dangerous_output_dir_rejected", "passed": passed, **result} + + +def test_zip_skips_symlink() -> dict: + case_root = TMP / "symlink-zip" + skill = write_skill(case_root) + outside_secret = case_root / "outside-secret.txt" + outside_secret.write_text("SECRET_FROM_OUTSIDE", encoding="utf-8") + (skill / "linked-secret.txt").symlink_to(outside_secret) + out_dir = case_root / "dist" + result = run( + [ + sys.executable, + str(CROSS_PACKAGER), + str(skill), + "--platform", + "generic", + "--output-dir", + str(out_dir), + "--zip", + ] + ) + zip_path = out_dir / "secure-demo.zip" + leaked = False + contains_link = False + if zip_path.exists(): + with zipfile.ZipFile(zip_path) as zf: + for name in zf.namelist(): + if name.endswith("linked-secret.txt"): + contains_link = True + leaked = zf.read(name).decode("utf-8") == "SECRET_FROM_OUTSIDE" + passed = result["ok"] and not contains_link and not leaked + return {"name": "zip_skips_symlink", "passed": passed, "contains_link": contains_link, "leaked": leaked, **result} + + +def test_output_dir_symlink_rejected() -> dict: + case_root = TMP / "symlink-output" + skill = write_skill(case_root) + real_out = case_root / "real-output" + real_out.mkdir(parents=True) + symlink_out = case_root / "linked-output" + symlink_out.symlink_to(real_out, target_is_directory=True) + result = run( + [ + sys.executable, + str(CROSS_PACKAGER), + str(skill), + "--platform", + "generic", + "--output-dir", + str(symlink_out), + ] + ) + passed = result["returncode"] == 2 and "Refusing symlink output directory" in result["stdout"] + return {"name": "output_dir_symlink_rejected", "passed": passed, **result} + + +def test_init_rejects_path_traversal() -> dict: + case_root = TMP / "init-traversal" + output_dir = case_root / "container" + outside = case_root / "outside-target" + result = run( + [ + sys.executable, + str(INIT_SKILL), + "../outside-target", + "--description", + "Traversal demo.", + "--output-dir", + str(output_dir), + ] + ) + passed = result["returncode"] == 2 and not outside.exists() and "Invalid skill name" in result["stdout"] + return {"name": "init_rejects_path_traversal", "passed": passed, "outside_exists": outside.exists(), **result} + + +def test_update_rejects_file_url() -> dict: + remote_version = TMP / "remote-version.txt" + remote_version.write_text("9.9.9\n", encoding="utf-8") + result = run( + [ + sys.executable, + str(CHECK_UPDATE), + "--force", + "--no-cache", + "--version-url", + remote_version.as_uri(), + "--manifest-url", + remote_version.as_uri(), + ] + ) + passed = result["returncode"] == 2 and "Update URL scheme is not allowed: file" in result["stdout"] + return {"name": "update_rejects_file_url", "passed": passed, **result} + + +def test_openai_yaml_uses_safe_dump() -> dict: + case_root = TMP / "safe-yaml" + skill = write_skill(case_root) + interface_path = skill / "agents" / "interface.yaml" + payload = yaml.safe_load(interface_path.read_text(encoding="utf-8")) + payload["interface"]["display_name"] = 'Quoted "Demo"\nSecond line' + payload["interface"]["default_prompt"] = 'Use "quotes" and:\n- preserve YAML' + interface_path.write_text(yaml.safe_dump(payload, sort_keys=False, allow_unicode=True), encoding="utf-8") + out_dir = case_root / "dist" + result = run( + [ + sys.executable, + str(CROSS_PACKAGER), + str(skill), + "--platform", + "openai", + "--output-dir", + str(out_dir), + ] + ) + generated_yaml = out_dir / "targets" / "openai" / "agents" / "openai.yaml" + parsed = yaml.safe_load(generated_yaml.read_text(encoding="utf-8")) if generated_yaml.exists() else {} + passed = ( + result["ok"] + and parsed.get("interface", {}).get("display_name") == payload["interface"]["display_name"] + and parsed.get("interface", {}).get("default_prompt") == payload["interface"]["default_prompt"] + ) + return {"name": "openai_yaml_uses_safe_dump", "passed": passed, "parsed": parsed, **result} + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + cases = [ + test_dangerous_output_dir(), + test_zip_skips_symlink(), + test_output_dir_symlink_rejected(), + test_init_rejects_path_traversal(), + test_update_rejects_file_url(), + test_openai_yaml_uses_safe_dump(), + ] + report = {"ok": all(case["passed"] for case in cases), "cases": cases} + print(json.dumps(report, ensure_ascii=False, indent=2)) + if not report["ok"]: + raise SystemExit(2) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_atlas.py b/tests/verify_skill_atlas.py new file mode 100644 index 0000000..92bed68 --- /dev/null +++ b/tests/verify_skill_atlas.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "build_skill_atlas.py" + + +def write_skill( + root: Path, + name: str, + description: str, + manifest: dict, + shared_reference: bool = False, +) -> Path: + skill_dir = root / name + skill_dir.mkdir(parents=True, exist_ok=True) + (skill_dir / "SKILL.md").write_text( + f"""--- +name: {name} +description: {description} +--- + +# {name} + +## Workflow + +1. Understand the request. +2. Produce the requested artifact. +3. Review the output. +""", + encoding="utf-8", + ) + (skill_dir / "manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + if shared_reference: + references = skill_dir / "references" + references.mkdir(parents=True, exist_ok=True) + (references / "release-method.md").write_text("# Release Method\n", encoding="utf-8") + return skill_dir + + +def write_adoption_report(skill_dir: Path, risk_band: str = "medium") -> None: + reports = skill_dir / "reports" + reports.mkdir(parents=True, exist_ok=True) + (reports / "adoption_drift_report.json").write_text( + json.dumps( + { + "ok": True, + "summary": { + "event_count": 4, + "adoption_sample_count": 3, + "activation_count": 2, + "accepted_count": 1, + "edited_count": 0, + "rejected_count": 1, + "missed_count": 1, + "failed_count": 1, + "adoption_rate": 33.3, + "missed_trigger_count": 1, + "wrong_trigger_count": 0, + "bad_output_count": 1, + "script_error_count": 1, + "missing_resource_count": 0, + "review_overdue_count": 0, + "risk_band": risk_band, + }, + "next_iteration_candidates": [ + { + "signal": "bad output", + "recommendation": "Convert the failed output shape into an Output Eval assertion.", + } + ], + "failures": [], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_skill_atlas" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + + alpha = write_skill( + tmp_root, + "alpha-release-skill", + "Use when turning release notes and changelog snippets into a reusable release brief for teams.", + { + "name": "alpha-release-skill", + "owner": "Release Team", + "maturity_tier": "production", + "review_cadence": "monthly", + "updated_at": "2025-01-01", + "target_platforms": ["openai", "generic"], + }, + shared_reference=True, + ) + beta = write_skill( + tmp_root, + "beta-release-skill", + "Use when turning changelog notes and release snippets into reusable release briefs for team review.", + { + "name": "beta-release-skill", + "maturity_tier": "production", + "updated_at": "2026-06-01", + "target_platforms": ["openai"], + }, + shared_reference=True, + ) + write_adoption_report(beta, risk_band="high") + write_skill( + tmp_root, + "gamma-doc-skill", + "Use when cleaning messy documentation notes into a concise internal knowledge-base article.", + { + "name": "gamma-doc-skill", + "owner": "Docs Team", + "maturity_tier": "scaffold", + "review_cadence": "quarterly", + "updated_at": "2026-05-01", + "target_platforms": ["generic"], + }, + ) + evals = tmp_root / "evals" + evals.mkdir() + (evals / "failure-cases.md").write_text( + "- no_route: repeated pricing approval prompts are missed by the current library.\n", + encoding="utf-8", + ) + policy_dir = tmp_root / "skill_atlas" + policy_dir.mkdir() + (policy_dir / "policy.json").write_text( + json.dumps( + { + "schema_version": "1.0", + "scope_rules": [ + { + "path_prefix": "beta-release-skill", + "scope": "example", + "actionable": False, + "reason": "fixture used to verify scoped portfolio warnings", + } + ], + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + + output_dir = tmp_root / "atlas" + report_html = tmp_root / "skill_atlas.html" + report_json = tmp_root / "skill_atlas.json" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--workspace-root", + str(tmp_root), + "--output-dir", + str(output_dir), + "--report-html", + str(report_html), + "--report-json", + str(report_json), + "--today", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + ) + assert proc.returncode == 0, proc.stderr or proc.stdout + payload = json.loads(proc.stdout) + summary = payload["summary"] + assert summary["skill_count"] == 3, summary + assert summary["actionable_skill_count"] == 2, summary + assert summary["route_collision_count"] >= 1, summary + assert summary["actionable_route_collision_count"] <= summary["route_collision_count"], summary + assert summary["owner_gap_count"] >= 1, summary + assert summary["stale_count"] >= 1, summary + assert summary["shared_resource_count"] >= 1, summary + assert summary["no_route_opportunity_count"] >= 2, summary + assert summary["drift_signal_count"] == 2, summary + assert summary["actionable_drift_signal_count"] == 1, summary + assert summary["telemetry_report_count"] == 1, summary + assert (output_dir / "catalog.json").exists(), output_dir + assert (output_dir / "route_overlap_matrix.csv").exists(), output_dir + assert (output_dir / "dependency_graph.json").exists(), output_dir + assert (output_dir / "stale_skills.json").exists(), output_dir + assert (output_dir / "owner_review_gaps.json").exists(), output_dir + assert (output_dir / "drift_signals.json").exists(), output_dir + no_route_path = output_dir / "no_route_opportunities.json" + assert no_route_path.exists(), output_dir + html = report_html.read_text(encoding="utf-8") + assert "Skill Atlas" in html, html[:1000] + assert "Route Collisions" in html, html[:3000] + assert "Actionable Issues" in html, html[:3000] + assert "Drift Signals" in html, html[:3000] + assert "No-Route Opportunities" in html, html[:3000] + assert "Missed-trigger telemetry" in html, html + catalog = json.loads((output_dir / "catalog.json").read_text(encoding="utf-8")) + assert catalog["summary"]["skill_count"] == 3, catalog + alpha_catalog = next(item for item in catalog["skills"] if item["name"] == alpha.name) + assert alpha_catalog["telemetry"]["risk_band"] == "missing", alpha_catalog + drift = json.loads((output_dir / "drift_signals.json").read_text(encoding="utf-8")) + assert any(item["name"] == alpha.name and item["signal_types"] == ["no telemetry"] for item in drift), drift + assert any(item["name"] == beta.name and "bad output" in item["signal_types"] and not item["actionable"] for item in drift), drift + no_route = json.loads(no_route_path.read_text(encoding="utf-8")) + assert {item["source_type"] for item in no_route} >= {"failure-case", "telemetry"}, no_route + telemetry_opportunity = next(item for item in no_route if item["source_type"] == "telemetry") + assert telemetry_opportunity["skill"] == beta.name, telemetry_opportunity + assert telemetry_opportunity["signal"] == "missed trigger", telemetry_opportunity + assert telemetry_opportunity["missed_trigger_count"] == 1, telemetry_opportunity + assert telemetry_opportunity["actionable"] is False, telemetry_opportunity + assert "metadata-only telemetry" in telemetry_opportunity["privacy_contract"], telemetry_opportunity + assert "raw prompt" in telemetry_opportunity["privacy_contract"], telemetry_opportunity + assert payload["scope_policy"]["present"], payload["scope_policy"] + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_interpretation.py b/tests/verify_skill_interpretation.py new file mode 100644 index 0000000..95fffa4 --- /dev/null +++ b/tests/verify_skill_interpretation.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" +TMP = ROOT / "tests" / "tmp_skill_interpretation" + + +def run(*args: str) -> dict: + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + return {"ok": proc.returncode == 0, "payload": payload, "stderr": proc.stderr} + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + init_result = run( + "init", + "interpretation-demo-skill", + "--description", + "Turn messy launch notes into a reusable launch checklist skill with review gates.", + "--output-dir", + str(TMP), + "--mode", + "production", + "--archetype", + "production", + ) + assert init_result["ok"], init_result + skill_dir = Path(init_result["payload"]["root"]) + assert (skill_dir / "reports" / "skill-overview.html").exists(), skill_dir + assert (skill_dir / "reports" / "skill-interpretation.html").exists(), skill_dir + assert (skill_dir / "reports" / "skill-interpretation.json").exists(), skill_dir + report_view = init_result["payload"]["report_view"] + assert report_view["interpretation_report"].endswith("reports/skill-interpretation.html"), report_view + assert "reports/skill-interpretation.html" in report_view["message"], report_view + + rerender_result = run("skill-interpretation", str(skill_dir)) + assert rerender_result["ok"], rerender_result + assert rerender_result["payload"]["artifacts"]["html"].endswith("reports/skill-interpretation.html"), rerender_result + assert rerender_result["payload"]["summary"]["report_kind"] == "skill-interpretation", rerender_result + assert rerender_result["payload"]["summary"]["default_language"] == "zh-CN", rerender_result + assert rerender_result["payload"]["summary"]["section_count"] == 9, rerender_result + assert rerender_result["payload"]["summary"]["source_model_reused"] is True, rerender_result + + payload = json.loads((skill_dir / "reports" / "skill-interpretation.json").read_text(encoding="utf-8")) + assert payload["report_contract"]["schema_version"] == "2.0", payload["report_contract"] + assert payload["report_contract"]["report_kind"] == "skill-interpretation", payload["report_contract"] + assert payload["report_contract"]["html_report"] == "reports/skill-interpretation.html", payload["report_contract"] + assert payload["report_contract"]["default_language"] == "zh-CN", payload["report_contract"] + assert payload["report_contract"]["languages"] == ["zh-CN", "en"], payload["report_contract"] + assert payload["report_contract"]["layout"] == "kami-white-audit-v2", payload["report_contract"] + assert payload["interpretation_contract"]["source_model"] == "skill-overview-v2", payload["interpretation_contract"] + assert payload["interpretation_contract"]["source_model_reused"] is True, payload["interpretation_contract"] + assert len(payload["interpretation_contract"]["includes"]) >= 10, payload["interpretation_contract"] + assert all(len(label) == 4 for label in payload["report_contract"]["nav_labels"]), payload["report_contract"] + assert "reports/skill-interpretation.html" in payload["skill_summary"]["deliverables"], payload["skill_summary"] + + html = (skill_dir / "reports" / "skill-interpretation.html").read_text(encoding="utf-8") + assert 'data-report-lang="zh-CN"' in html, html[:500] + assert 'class="language-switch"' in html, html[:2600] + assert "reports/skill-interpretation.html" in html, html[:10000] + assert "After creation, open reports/skill-interpretation.html before expanding the package further." in html, html[:12000] + for label in ("技能概述", "总览指标", "能力画像", "原理结构", "契约边界", "质量评估", "风险治理", "包体资产", "迭代路线"): + assert f">{label}" in html, label + for chart_label in ("评分雷达", "交付流程", "能力矩阵", "风险热力", "资产分布", "迭代时间"): + assert chart_label in html, chart_label + + schema = json.loads((ROOT / "schemas" / "skill-interpretation.schema.json").read_text(encoding="utf-8")) + assert schema["properties"]["report_contract"]["properties"]["report_kind"]["const"] == "skill-interpretation", schema + assert schema["properties"]["interpretation_contract"]["properties"]["source_model_reused"]["const"] is True, schema + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_ir.py b/tests/verify_skill_ir.py new file mode 100644 index 0000000..914222f --- /dev/null +++ b/tests/verify_skill_ir.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "export_skill_ir.py" + + +def main() -> None: + output_path = ROOT / "tests" / "tmp_skill_ir" / "yao-meta-skill.json" + output_path.parent.mkdir(parents=True, exist_ok=True) + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_path), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + result = json.loads(proc.stdout) + assert result["ok"], result + assert result["summary"]["name"] == "yao-meta-skill", result + assert result["summary"]["trigger_samples"] >= 3, result + assert output_path.exists(), output_path + + payload = json.loads(output_path.read_text(encoding="utf-8")) + assert payload["schema_version"] == "2.0.0", payload + assert payload["name"] == "yao-meta-skill", payload + assert payload["job_to_be_done"], payload + assert payload["trigger_surface"]["description"], payload + assert payload["trigger_surface"]["should_trigger"], payload + assert payload["trigger_surface"]["should_not_trigger"], payload + assert payload["trigger_surface"]["edge_cases"], payload + assert payload["workflow"]["steps"], payload + assert payload["workflow"]["decision_points"], payload + assert any("`Governed`" in item for item in payload["workflow"]["decision_points"]), payload["workflow"]["decision_points"] + assert payload["resources"]["references"], payload + assert payload["resources"]["scripts"], payload + expected_scripts = { + str(path.relative_to(ROOT)) + for path in (ROOT / "scripts").rglob("*") + if path.is_file() and path.suffix in {".py", ".sh", ".js", ".ts"} + } + actual_scripts = set(payload["resources"]["scripts"]) + assert expected_scripts <= actual_scripts, sorted(expected_scripts - actual_scripts) + assert "assets/skill-overview.css" in payload["resources"]["assets"], payload["resources"]["assets"] + assert "assets/skill-overview.js" in payload["resources"]["assets"], payload["resources"]["assets"] + assert "assets/review-studio.css" in payload["resources"]["assets"], payload["resources"]["assets"] + assert "assets/review-viewer.css" in payload["resources"]["assets"], payload["resources"]["assets"] + assert payload["resources"]["reports"], payload + assert "evals/trigger_cases.json" in payload["eval_plan"]["trigger"], payload["eval_plan"] + assert payload["risk"]["trust_boundary"] == "external", payload + assert payload["governance"]["maturity"] == "governed", payload + assert "openai" in payload["targets"], payload + + validate_proc = subprocess.run( + [sys.executable, str(SCRIPT), str(ROOT), "--validate-only"], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + validate_result = json.loads(validate_proc.stdout) + assert validate_result["ok"], validate_result + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_ir_paths.py b/tests/verify_skill_ir_paths.py new file mode 100644 index 0000000..b781b7e --- /dev/null +++ b/tests/verify_skill_ir_paths.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import json +import shutil +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "scripts")) + +from skill_ir_paths import candidate_paths, find_skill_ir, find_skill_ir_path # noqa: E402 + + +TMP = ROOT / "tests" / "tmp_skill_ir_paths" + + +def write_json(path: Path, payload: dict) -> 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 main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + + root_ir, root_path = find_skill_ir(ROOT, "yao-meta-skill", require_schema=True) + assert root_path == "skill-ir/examples/yao-meta-skill.json", root_path + assert root_ir["schema_version"] == "2.0.0", root_ir + assert find_skill_ir_path(ROOT, "yao-meta-skill", require_schema=True) == root_path + + preferred = TMP / "reports" / "skill-ir.json" + example = TMP / "skill-ir" / "examples" / "demo-skill.json" + write_json(preferred, {"schema_version": "2.0.0", "name": "reports-copy"}) + write_json(example, {"schema_version": "2.0.0", "name": "example-copy"}) + payload, source = find_skill_ir(TMP, "demo-skill", require_schema=True) + assert source == "reports/skill-ir.json", source + assert payload["name"] == "reports-copy", payload + + preferred.unlink() + payload, source = find_skill_ir(TMP, "demo-skill", require_schema=True) + assert source == "skill-ir/examples/demo-skill.json", source + assert payload["name"] == "example-copy", payload + + example.unlink() + write_json(TMP / "skill-ir" / "examples" / "zzz-extra.json", {"schema_version": "2.0.0", "name": "wildcard"}) + payload, source = find_skill_ir(TMP, "missing-name", require_schema=True) + assert source == "skill-ir/examples/zzz-extra.json", source + assert payload["name"] == "wildcard", payload + + (TMP / "skill-ir" / "examples" / "zzz-extra.json").unlink() + write_json(TMP / "reports" / "skill-ir.json", {"name": "no-schema"}) + assert find_skill_ir_path(TMP, "demo-skill", require_schema=True, fallback_source="missing") == "missing" + assert find_skill_ir_path(TMP, "demo-skill", require_schema=False) == "reports/skill-ir.json" + + paths = [str(path.relative_to(TMP)) for path in candidate_paths(TMP, "demo-skill")] + assert paths[:3] == [ + "reports/skill-ir.json", + "skill-ir/examples/demo-skill.json", + "skill-ir/examples/tmp_skill_ir_paths.json", + ], paths + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_os2_audit.py b/tests/verify_skill_os2_audit.py new file mode 100644 index 0000000..271b665 --- /dev/null +++ b/tests/verify_skill_os2_audit.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_skill_os2_audit.py" +TMP = ROOT / "tests" / "tmp_skill_os2_audit" + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + output_json = TMP / "skill_os2_audit.json" + output_md = TMP / "skill_os2_audit.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + assert payload["summary"]["decision"] == "continue-iteration", payload + assert payload["summary"]["world_class_ready"] is False, payload + assert payload["summary"]["missing_count"] == 0, payload + assert payload["summary"]["item_count"] >= 15, payload + assert payload["summary"]["pass_count"] >= 12, payload + assert payload["summary"]["human_required_count"] >= 1, payload + assert payload["summary"]["external_required_count"] >= 2, payload + assert payload["artifacts"]["world_class_evidence_plan"] == "reports/world_class_evidence_plan.md", payload + items = {item["key"]: item for item in payload["items"]} + assert items["skill-ir"]["status"] == "pass", items["skill-ir"] + assert items["target-compiler"]["status"] == "pass", items["target-compiler"] + assert items["output-eval-lab"]["status"] == "pass", items["output-eval-lab"] + assert items["benchmark-reproducibility"]["status"] == "pass", items["benchmark-reproducibility"] + assert items["provider-holdout"]["status"] == "pass", items["provider-holdout"] + assert "model-executed 10" in items["provider-holdout"]["current"], items["provider-holdout"] + assert items["human-adjudication"]["status"] == "human_required", items["human-adjudication"] + assert items["native-permission-enforcement"]["status"] == "external_required", items["native-permission-enforcement"] + assert "installer-enforced targets" in items["native-permission-enforcement"]["current"], items["native-permission-enforcement"] + assert items["native-client-telemetry"]["status"] == "external_required", items["native-client-telemetry"] + assert any(entry["path"] == "scripts/provider_output_eval_runner.py" and entry["exists"] for entry in items["provider-holdout"]["evidence"]) + assert any(entry["path"] == "reports/benchmark_reproducibility.json" and entry["exists"] for entry in items["benchmark-reproducibility"]["evidence"]) + assert any(entry["path"] == "scripts/telemetry_native_host.py" and entry["exists"] for entry in items["native-client-telemetry"]["evidence"]) + markdown = output_md.read_text(encoding="utf-8") + assert "Skill OS 2.0 Audit" in markdown, markdown + assert "reports/world_class_evidence_plan.md" in markdown, markdown + assert "Provider Holdout" in markdown, markdown + assert "Benchmark Reproducibility" in markdown, markdown + assert "`human-adjudication`" in markdown, markdown + assert "`native-client-telemetry`" in markdown, markdown + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_os2_coverage.py b/tests/verify_skill_os2_coverage.py new file mode 100644 index 0000000..5c580dc --- /dev/null +++ b/tests/verify_skill_os2_coverage.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_skill_os2_coverage.py" +TMP = ROOT / "tests" / "tmp_skill_os2_coverage" + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + output_json = TMP / "skill_os2_coverage.json" + output_md = TMP / "skill_os2_coverage.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-14", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + summary = payload["summary"] + assert summary["decision"] == "local-blueprint-covered-evidence-pending", summary + assert summary["module_count"] == 8, summary + assert summary["recommended_pr_count"] == 13, summary + assert summary["item_count"] == 21, summary + assert summary["pass_count"] == 21, summary + assert summary["warn_count"] == 0, summary + assert summary["missing_count"] == 0, summary + assert summary["extension_track_count"] == 4, summary + assert summary["extension_partial_count"] == 0, summary + assert summary["extension_planned_count"] == 0, summary + assert summary["extension_covered_count"] == 4, summary + assert summary["adaptive_extension_ready"] is True, summary + assert summary["local_blueprint_ready"] is True, summary + assert summary["public_world_class_ready"] is False, summary + assert summary["world_class_evidence_pending_count"] == 4, summary + modules = {item["key"]: item for item in payload["modules"]} + assert set(modules) == { + "skill-ir", + "output-eval-lab", + "runtime-conformance", + "trust-security", + "skill-atlas", + "registry-distribution", + "review-studio", + "telemetry-drift", + }, modules + assert modules["skill-ir"]["status"] == "pass", modules["skill-ir"] + assert modules["review-studio"]["status"] == "pass", modules["review-studio"] + assert any(entry["path"] == "reports/review-studio.html" and entry["exists"] for entry in modules["review-studio"]["evidence"]) + prs = {item["key"]: item for item in payload["recommended_prs"]} + assert set(prs) == { + "benchmark-methodology", + "output-eval-schema", + "output-eval-runner", + "output-quality-scorecard", + "skill-ir-v0", + "compiler-refactor", + "agent-skills-conformance", + "trust-check", + "skill-atlas-generator", + "registry-package-format", + "review-studio-2", + "migration-v2-docs", + "evidence-consistency", + }, prs + assert prs["agent-skills-conformance"]["status"] == "pass", prs["agent-skills-conformance"] + assert prs["benchmark-methodology"]["status"] == "pass", prs["benchmark-methodology"] + assert prs["evidence-consistency"]["status"] == "pass", prs["evidence-consistency"] + assert any(entry["path"] == "reports/benchmark_reproducibility.json" and entry["exists"] for entry in prs["benchmark-methodology"]["evidence"]) + assert payload["source_blueprint"]["core_module_count"] == 8, payload + assert payload["source_blueprint"]["recommended_pr_count"] == 13, payload + assert payload["source_blueprint"]["reference_extension_count"] == 4, payload + extension_tracks = {item["key"]: item for item in payload["reference_extension_tracks"]} + assert extension_tracks["skill-interpretation-report"]["status"] == "covered", extension_tracks + assert extension_tracks["adaptive-self-iteration"]["status"] == "covered", extension_tracks + assert extension_tracks["daily-skillops-report"]["status"] == "covered", extension_tracks + assert extension_tracks["weekly-curator-report"]["status"] == "covered", extension_tracks + assert any( + entry["path"] == "reports/skill-overview.html" and entry["exists"] + for entry in extension_tracks["skill-interpretation-report"]["evidence"] + ), extension_tracks["skill-interpretation-report"] + assert any( + entry["path"] == "scripts/render_skill_interpretation.py" and entry["exists"] + for entry in extension_tracks["skill-interpretation-report"]["evidence"] + ), extension_tracks["skill-interpretation-report"] + assert any( + entry["path"] == "schemas/skill-interpretation.schema.json" and entry["exists"] + for entry in extension_tracks["skill-interpretation-report"]["evidence"] + ), extension_tracks["skill-interpretation-report"] + assert any( + entry["path"] == "scripts/summarize_user_signals.py" and entry["exists"] + for entry in extension_tracks["adaptive-self-iteration"]["evidence"] + ), extension_tracks["adaptive-self-iteration"] + assert any( + entry["path"] == "scripts/apply_adaptation.py" and entry["exists"] + for entry in extension_tracks["adaptive-self-iteration"]["evidence"] + ), extension_tracks["adaptive-self-iteration"] + assert any( + entry["path"] == "reports/adaptation_approval_ledger.json" and entry["exists"] + for entry in extension_tracks["adaptive-self-iteration"]["evidence"] + ), extension_tracks["adaptive-self-iteration"] + assert any( + entry["path"] == "reports/adaptation_regression_report.json" and entry["exists"] + for entry in extension_tracks["adaptive-self-iteration"]["evidence"] + ), extension_tracks["adaptive-self-iteration"] + assert any( + entry["path"] == "scripts/render_daily_skillops_report.py" and entry["exists"] + for entry in extension_tracks["daily-skillops-report"]["evidence"] + ), extension_tracks["daily-skillops-report"] + assert any( + entry["path"] == "tests/verify_daily_skillops.py" and entry["exists"] + for entry in extension_tracks["daily-skillops-report"]["evidence"] + ), extension_tracks["daily-skillops-report"] + assert any( + entry["path"].startswith("reports/skillops/daily/") + and entry["path"].endswith(".json") + and entry["exists"] + for entry in extension_tracks["daily-skillops-report"]["evidence"] + ), extension_tracks["daily-skillops-report"] + assert any( + entry["path"].startswith("reports/skillops/daily/") + and entry["path"].endswith(".md") + and entry["exists"] + for entry in extension_tracks["daily-skillops-report"]["evidence"] + ), extension_tracks["daily-skillops-report"] + assert any( + entry["path"] == "scripts/render_weekly_curator_report.py" and entry["exists"] + for entry in extension_tracks["weekly-curator-report"]["evidence"] + ), extension_tracks["weekly-curator-report"] + assert any( + entry["path"] == "tests/verify_weekly_curator.py" and entry["exists"] + for entry in extension_tracks["weekly-curator-report"]["evidence"] + ), extension_tracks["weekly-curator-report"] + assert any( + entry["path"].startswith("reports/skillops/weekly/") + and entry["path"].endswith(".json") + and entry["exists"] + for entry in extension_tracks["weekly-curator-report"]["evidence"] + ), extension_tracks["weekly-curator-report"] + assert any( + entry["path"].startswith("reports/skillops/weekly/") + and entry["path"].endswith(".md") + and entry["exists"] + for entry in extension_tracks["weekly-curator-report"]["evidence"] + ), extension_tracks["weekly-curator-report"] + assert "Close the four world-class evidence ledger entries" in payload["next_highest_leverage"][0], payload + assert "skill interpretation report" in " ".join(payload["next_highest_leverage"]), payload + assert "Daily SkillOps" in " ".join(payload["next_highest_leverage"]), payload + markdown = output_md.read_text(encoding="utf-8") + assert "Skill OS 2.0 Blueprint Coverage" in markdown, markdown + assert "local blueprint ready: `true`" in markdown, markdown + assert "public world-class ready: `false`" in markdown, markdown + assert "extension covered: `4`" in markdown, markdown + assert "extension partial: `0`" in markdown, markdown + assert "## Core Modules" in markdown, markdown + assert "## Recommended PR Coverage" in markdown, markdown + assert "Evidence Consistency" in markdown, markdown + assert "## Reference Extension Tracks" in markdown, markdown + assert "Skill Interpretation Report" in markdown, markdown + assert "Adaptive Self-Iteration" in markdown, markdown + assert "Daily SkillOps Report" in markdown, markdown + assert "Weekly Curator Report" in markdown, markdown + assert "user-supplied 2.0 reference plan" in markdown, markdown + assert "`agent-skills-conformance`" not in markdown, markdown + assert "Agent Skills Conformance" in markdown, markdown + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_overview.py b/tests/verify_skill_overview.py new file mode 100644 index 0000000..05d17b2 --- /dev/null +++ b/tests/verify_skill_overview.py @@ -0,0 +1,392 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" +sys.path.insert(0, str(ROOT / "scripts")) + +from skill_report_layout import render_language_switch, render_report_nav, skill_overview_css, skill_overview_script +from skill_report_i18n import en_for +from skill_report_model import REPORT_NAV_V2, build_report_model + + +def run(*args: str) -> dict: + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stderr": proc.stderr, + } + + +def main() -> None: + nav_contract = render_report_nav(REPORT_NAV_V2) + assert nav_contract.count("技能概述" in nav_contract, nav_contract + assert ">Overview" in nav_contract, nav_contract + assert render_report_nav([]) == "" + + language_switch_contract = render_language_switch() + assert 'data-set-lang="zh-CN"' in language_switch_contract, language_switch_contract + assert 'data-set-lang="en"' in language_switch_contract, language_switch_contract + assert 'aria-pressed="true"' in language_switch_contract, language_switch_contract + + css_contract = skill_overview_css() + assert css_contract == (ROOT / "assets" / "skill-overview.css").read_text(encoding="utf-8").strip() + assert "position: sticky" in css_contract, css_contract[:1200] + assert ".report-nav {" in css_contract, css_contract[:3200] + assert "background: #ffffff" in css_contract, css_contract[:1600] + assert ".section-body" in css_contract, css_contract[:7000] + assert ".metrics-primary" in css_contract, css_contract[:7000] + assert ".metrics-flow" in css_contract, css_contract[:9000] + assert ".metric-detail-grid" in css_contract, css_contract[:9000] + assert ".metric-detail-section" in css_contract, css_contract[:9000] + assert ".detail-section-kicker" in css_contract, css_contract[:9000] + assert ".section-head {\n display: grid;\n grid-template-columns: minmax(0, 1fr);" in css_contract, css_contract[:7000] + assert "grid-template-columns: minmax(0, 246px) minmax(0, 1fr)" not in css_contract, css_contract[:7000] + assert ".metrics-primary {\n display: grid;\n grid-template-columns: minmax(360px, 0.92fr) minmax(0, 1.08fr);" in css_contract, css_contract[:9000] + assert ".metric-detail-grid {\n grid-template-columns: repeat(2, minmax(min(100%, 420px), 1fr));" in css_contract, css_contract[:9000] + assert ".metric-card {\n display: grid;\n grid-template-columns: minmax(112px, 0.28fr) minmax(0, 1fr);" in css_contract, css_contract[:11000] + assert "repeat(auto-fit, minmax(min(100%, 320px), 1fr))" not in css_contract, css_contract[:9000] + assert "grid-template-columns: minmax(420px, 1fr)" not in css_contract, css_contract[:9000] + assert "overflow-wrap: break-word" in css_contract, css_contract[:9000] + assert ".metrics-primary, .metric-detail-grid, .two-col" in css_contract, css_contract[-2600:] + assert "@media (max-width: 980px)" in css_contract, css_contract[-2200:] + + script_contract = skill_overview_script() + assert script_contract == (ROOT / "assets" / "skill-overview.js").read_text(encoding="utf-8").strip() + assert 'setLanguage("zh-CN")' in script_contract, script_contract[-1000:] + assert "scaleX(" in script_contract, script_contract + assert "aria-current" in script_contract, script_contract + + assert en_for("把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。") == ( + "Turn one-off experience into a reusable, evaluable, and portable skill package." + ) + assert en_for("交付结果:SKILL.md, agents/interface.yaml") == "Deliverables: SKILL.md, agents/interface.yaml" + assert en_for("已生成 12 / 20 类报告证据。") == "Generated 12 / 20 evidence report types." + assert en_for("skill-ir.json 已存在。") == "skill-ir.json exists." + assert en_for("compiled_targets.json 已存在。") == "compiled_targets.json exists." + assert en_for("reports 未发现或为空,完整度扣分。") == ( + "reports was not found or is empty, reducing completeness." + ) + assert en_for("evals/ 证据不足。") == "evals/ evidence is insufficient." + assert en_for("用户提供的工作流、提示词、文档、记录或散乱笔记") == ( + "User-provided workflows, prompts, documents, records, or rough notes." + ) + assert en_for("期望沉淀的复用场景、排除项、约束和质量标准") == ( + "The reusable scenario, exclusions, constraints, and quality standards to capture." + ) + assert en_for("可路由的 SKILL.md") == "A routeable SKILL.md." + assert en_for("agents/interface.yaml 元数据") == "agents/interface.yaml metadata." + assert en_for("必要的 references、scripts、evals、reports 证据") == ( + "Necessary references, scripts, evals, and reports evidence." + ) + assert en_for("入口约 864 个词/字,references 约 565 个词/字。") == ( + "Entrypoint is about 864 words/characters; references are about 565." + ) + assert en_for("Use this skill when the request matches: 中文描述") == ( + "Use this skill when the request matches the frontmatter description." + ) + assert en_for("补齐世界证据") == "Close world-class evidence" + assert en_for("世界级证据仍有 2 项待补;公开完成态 claim 必须继续保持阻塞。") == ( + "World-class evidence still has 2 pending item(s); public completion claims must stay blocked." + ) + assert en_for("补齐提供商留出证据:缺少真实 provider 模型运行和 token metadata。") == ( + "Close provider holdout evidence: Missing a real provider model run and token metadata." + ) + assert en_for("提交有效 intake packet,并让 ledger 通过 artifact SHA-256 校验。") == ( + "Submit valid intake packets and let the ledger verify artifact SHA-256 digests." + ) + + root_model = build_report_model(ROOT) + assert root_model["skill_ir"]["source_path"] == "skill-ir/examples/yao-meta-skill.json", root_model["skill_ir"] + assert "skill-ir/examples/yao-meta-skill.json" in root_model["skill_summary"]["deliverables"], root_model[ + "skill_summary" + ] + assert "reports/skill-ir.json" not in root_model["skill_summary"]["deliverables"], root_model["skill_summary"] + + tmp_root = ROOT / "tests" / "tmp_skill_overview" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + init_result = run( + "init", + "skill-overview-demo", + "--description", + "Turn rough requests into a compact reusable demo skill.", + "--output-dir", + str(tmp_root), + ) + assert init_result["ok"], init_result + + created = tmp_root / "skill-overview-demo" + assert (created / "README.md").exists(), created + assert (created / "manifest.json").exists(), created + assert (created / "reports" / "intent-dialogue.md").exists(), created + assert (created / "reports" / "intent-dialogue.json").exists(), created + assert (created / "reports" / "intent-confidence.md").exists(), created + assert (created / "reports" / "intent-confidence.json").exists(), created + assert (created / "reports" / "skill-overview.html").exists(), created + assert (created / "reports" / "skill-overview.json").exists(), created + assert (created / "reports" / "skill-interpretation.html").exists(), created + assert (created / "reports" / "skill-interpretation.json").exists(), created + assert (created / "reports" / "compiled_targets.md").exists(), created + assert (created / "reports" / "compiled_targets.json").exists(), created + assert (created / "reports" / "reference-synthesis.md").exists(), created + assert (created / "reports" / "reference-synthesis.json").exists(), created + assert (created / "reports" / "artifact-design-profile.md").exists(), created + assert (created / "reports" / "artifact-design-profile.json").exists(), created + assert (created / "reports" / "prompt-quality-profile.md").exists(), created + assert (created / "reports" / "prompt-quality-profile.json").exists(), created + assert (created / "reports" / "system-model.md").exists(), created + assert (created / "reports" / "system-model.json").exists(), created + assert (created / "reports" / "iteration-directions.md").exists(), created + assert (created / "reports" / "iteration-directions.json").exists(), created + assert (created / "reports" / "adoption_drift_report.md").exists(), created + assert (created / "reports" / "adoption_drift_report.json").exists(), created + assert (created / "reports" / "review_waivers.md").exists(), created + assert (created / "reports" / "review_waivers.json").exists(), created + + overview_json = json.loads((created / "reports" / "skill-overview.json").read_text(encoding="utf-8")) + directions_json = json.loads((created / "reports" / "iteration-directions.json").read_text(encoding="utf-8")) + assert overview_json["report_contract"]["schema_version"] == "2.0", overview_json.get("report_contract") + assert overview_json["report_contract"]["layout"] == "kami-white-audit-v2", overview_json.get("report_contract") + expected_v2_keys = { + "skill_summary", + "scorecard", + "capability_profile", + "principle_model", + "contract_boundary", + "quality_review", + "risk_governance", + "package_assets", + "iteration_roadmap", + "report_contract", + } + assert expected_v2_keys.issubset(overview_json.keys()), overview_json.keys() + assert "reports/skill-ir.json" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert overview_json["skill_ir"]["source_path"] == "reports/skill-ir.json", overview_json["skill_ir"] + assert "reports/compiled_targets.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/output_quality_scorecard.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/output_blind_review_pack.md" not in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/benchmark_reproducibility.md" not in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/world_class_evidence_ledger.md" not in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/conformance_matrix.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/security_trust_report.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/runtime_permission_probes.md" not in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/skill_atlas.html" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/registry_audit.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/package_verification.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/install_simulation.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/upgrade_check.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/adoption_drift_report.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/review_waivers.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/review-studio.html" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert "reports/skill-interpretation.html" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert overview_json["skill_ir"]["schema_version"] in {"", "2.0.0"}, overview_json.get("skill_ir") + assert overview_json["compiled_targets"]["summary"]["target_count"] >= 3, overview_json.get("compiled_targets") + assert overview_json["compiled_targets"]["summary"]["block_count"] == 0, overview_json.get("compiled_targets") + assert "output_quality" in overview_json, overview_json.keys() + assert "output_execution" in overview_json, overview_json.keys() + assert overview_json["output_execution"]["summary"] == {}, overview_json["output_execution"] + assert "output_blind_review" in overview_json, overview_json.keys() + assert overview_json["output_blind_review"]["summary"] == {}, overview_json["output_blind_review"] + assert "output_review_adjudication" in overview_json, overview_json.keys() + assert overview_json["output_review_adjudication"]["summary"] == {}, overview_json["output_review_adjudication"] + assert "benchmark_reproducibility" in overview_json, overview_json.keys() + assert overview_json["benchmark_reproducibility"]["summary"] == {}, overview_json["benchmark_reproducibility"] + assert "world_class_evidence_ledger" in overview_json, overview_json.keys() + assert overview_json["world_class_evidence_ledger"]["summary"] == {}, overview_json["world_class_evidence_ledger"] + assert "world_class_readiness" in overview_json, overview_json.keys() + assert overview_json["world_class_readiness"]["entry_count"] == 0, overview_json["world_class_readiness"] + assert overview_json["world_class_readiness"]["decision"] == "not-generated", overview_json["world_class_readiness"] + assert "runtime_conformance" in overview_json, overview_json.keys() + assert "runtime_permissions" in overview_json, overview_json.keys() + assert overview_json["runtime_permissions"]["summary"] == {}, overview_json["runtime_permissions"] + assert "trust_security" in overview_json, overview_json.keys() + assert "skill_atlas" in overview_json, overview_json.keys() + assert "registry_distribution" in overview_json, overview_json.keys() + assert "package_verification" in overview_json, overview_json.keys() + assert "install_simulation" in overview_json, overview_json.keys() + assert "upgrade_check" in overview_json, overview_json.keys() + assert "adoption_drift" in overview_json, overview_json.keys() + assert "review_waivers" in overview_json, overview_json.keys() + assert [item["title"] for item in overview_json["iteration_roadmap"]["items"]] == [ + item["title"] for item in directions_json["directions"] + ], { + "overview_roadmap": overview_json["iteration_roadmap"], + "iteration_directions": directions_json["directions"], + } + + initial_report_html = (created / "reports" / "skill-overview.html").read_text(encoding="utf-8") + assert directions_json["directions"][0]["title"] in initial_report_html, initial_report_html[:5000] + assert "世界证据" in initial_report_html, initial_report_html + assert "No world-class ledger has been generated" in initial_report_html, initial_report_html + assert "Skill-specific source text is authored in Chinese" not in initial_report_html, initial_report_html + + sample_ledger = { + "schema_version": "1.0", + "ok": True, + "summary": { + "ledger_entry_count": 2, + "accepted_count": 0, + "pending_count": 2, + "external_pending_count": 1, + "human_pending_count": 1, + "source_check_count": 4, + "source_pass_count": 1, + "ready_to_claim_world_class": False, + "decision": "evidence-pending", + }, + "entries": [ + { + "key": "provider-holdout", + "label": "Provider Holdout", + "category": "external", + "status": "pending", + "source_checklist": [ + {"label": "Provider model run", "status": "blocked"}, + {"label": "Timing observed", "status": "pass"}, + ], + }, + { + "key": "human-adjudication", + "label": "Human Adjudication", + "category": "human", + "status": "pending", + "source_checklist": [ + {"label": "No pending decisions", "status": "blocked"}, + ], + }, + ], + } + (created / "reports" / "world_class_evidence_ledger.json").write_text( + json.dumps(sample_ledger, ensure_ascii=False, indent=2), + encoding="utf-8", + ) + (created / "reports" / "world_class_evidence_ledger.md").write_text( + "# World Class Evidence Ledger\n", + encoding="utf-8", + ) + + rerender_result = run("skill-report", str(created)) + assert rerender_result["ok"], rerender_result + assert rerender_result["payload"]["artifacts"]["html"].endswith("reports/skill-overview.html"), rerender_result + + report_html = (created / "reports" / "skill-overview.html").read_text(encoding="utf-8") + assert 'lang="zh-CN"' in report_html, report_html[:200] + assert 'data-report-lang="zh-CN"' in report_html, report_html[:400] + assert 'class="language-switch"' in report_html, report_html[:2400] + assert 'class="skip-link"' in report_html, report_html[:2400] + assert 'class="progress-bar"' in report_html, report_html[:2400] + assert 'aria-current' in report_html, report_html[-3000:] + assert 'class="section-body metrics-report"' in report_html, report_html[:9000] + assert 'class="metrics-flow"' in report_html, report_html[:9000] + assert 'class="metrics-primary"' in report_html, report_html[:9000] + assert 'aria-label="指标图表与成熟度摘要"' in report_html, report_html[:9000] + assert 'class="metrics-stack"' not in report_html, report_html[:9000] + assert 'class="metric-detail-section"' in report_html, report_html[:12000] + assert 'class="detail-section-kicker"' in report_html, report_html[:12000] + assert "指标明细" in report_html, report_html[:12000] + assert 'class="metric-grid metric-detail-grid"' in report_html, report_html[:12000] + assert report_html.index('class="metrics-primary"') < report_html.index('class="metric-grid metric-detail-grid"'), report_html[:12000] + assert "class='metric-summary-list'" in report_html, report_html[:9000] + assert "class='metric-card-head'" in report_html, report_html[:12000] + assert 'data-set-lang="zh-CN"' in report_html, report_html[:2600] + assert 'data-set-lang="en"' in report_html, report_html[:2800] + assert 'data-lang="zh-CN"' in report_html, report_html[:3200] + assert 'data-lang="en"' in report_html, report_html[:3400] + assert "position: sticky" in report_html, report_html[:1200] + assert "background: #ffffff" in report_html, report_html[:1600] + assert ".report-nav {" in report_html and "overflow-x: auto" in report_html, report_html[:5000] + assert "scaleX(" in report_html, report_html[-3000:] + for label in ("技能概述", "总览指标", "能力画像", "原理结构", "契约边界", "质量评估", "风险治理", "包体资产", "迭代路线"): + assert f">{label}" in report_html, label + assert len(label) == 4, label + assert 'aria-label="Skill principle flow"' in report_html, report_html[:2400] + for chart_label in ("评分雷达", "交付流程", "能力矩阵", "风险热力", "资产分布", "迭代时间"): + assert chart_label in report_html, chart_label + assert "技能名称" in report_html, report_html[:5000] + assert "成熟度" in report_html, report_html[:5000] + assert "更新时间" in report_html, report_html[:5000] + assert "Skill name" in report_html, report_html[:5000] + assert "Turn one-off experience into a reusable, evaluable, and portable skill package." in report_html, report_html[:9000] + assert "After creation, open reports/skill-overview.html before expanding the package further." in report_html, report_html[:9000] + assert "世界证据" in report_html, report_html + assert "证据待补" in report_html, report_html + assert "世界级证据尚未完成:2 项待补,0 项已接受。" in report_html, report_html + assert "World-class evidence is not complete: 2 pending, 0 accepted." in report_html, report_html + assert "提供商留出" in report_html, report_html + assert "人工盲评" in report_html, report_html + assert "Provider Holdout" in report_html, report_html + assert "阻塞检查" in report_html, report_html + assert "Blocked Checks" in report_html, report_html + assert "Provider model run" in report_html, report_html + assert "No pending decisions" in report_html, report_html + assert '把一次性经验沉淀为可复用、可评估、可迁移的 Skill 包体。' not in report_html, report_html[:9000] + assert '创建完成后建议先打开 reports/skill-overview.html,再继续扩展包体。' not in report_html, report_html[:9000] + assert "输入材料" in report_html, report_html[:3000] + assert "输出结果" in report_html, report_html[:3400] + assert "Top 3 Next Moves" not in report_html, report_html[:3800] + assert "下一步" in report_html, report_html[:4200] + + overview_json = json.loads((created / "reports" / "skill-overview.json").read_text(encoding="utf-8")) + assert "reports/world_class_evidence_ledger.md" in overview_json["skill_summary"]["deliverables"], overview_json["skill_summary"] + assert overview_json["world_class_readiness"]["entry_count"] == 2, overview_json["world_class_readiness"] + assert overview_json["world_class_readiness"]["pending_count"] == 2, overview_json["world_class_readiness"] + assert overview_json["world_class_readiness"]["source_pass_count"] == 1, overview_json["world_class_readiness"] + assert overview_json["world_class_readiness"]["source_check_count"] == 4, overview_json["world_class_readiness"] + assert overview_json["iteration_roadmap"]["items"][0]["title"] == "补齐世界证据", overview_json["iteration_roadmap"] + assert overview_json["iteration_roadmap"]["items"][0]["source"] == "world_class_evidence_ledger", overview_json["iteration_roadmap"] + assert "世界级证据仍有 2 项待补" in overview_json["iteration_roadmap"]["items"][0]["why"], overview_json["iteration_roadmap"] + assert any("补齐提供商留出证据" in item for item in overview_json["iteration_roadmap"]["items"][0]["actions"]), overview_json["iteration_roadmap"] + assert "Close world-class evidence" in report_html, report_html + assert "执行流程" in report_html, report_html[:5000] + assert "调用方式" in report_html, report_html[:5000] + assert "证据不足" in report_html or "证据充分" in report_html, report_html[:8000] + assert "执行证据" in report_html, report_html + assert "尚未生成输出执行证据报告" in report_html, report_html + assert "盲评审定" in report_html, report_html + assert "尚未生成盲评审定报告" in report_html, report_html + assert "原始说明可切换到英文查看;默认中文报告保留结论与结构说明。" not in report_html, report_html[:12000] + assert "理解用户请求" in report_html, report_html[:5000] + assert overview_json["logic_steps"][0] in report_html, overview_json.get("logic_steps") + assert overview_json["usage_steps"][0] in report_html, overview_json.get("usage_steps") + assert overview_json["report_contract"]["default_language"] == "zh-CN", overview_json.get("report_contract") + assert overview_json["report_contract"]["languages"] == ["zh-CN", "en"], overview_json.get("report_contract") + assert overview_json["report_contract"]["nav_labels"] == [ + "技能概述", + "总览指标", + "能力画像", + "原理结构", + "契约边界", + "质量评估", + "风险治理", + "包体资产", + "迭代路线", + ], overview_json.get("report_contract") + + intent_text = (created / "reports" / "intent-dialogue.md").read_text(encoding="utf-8") + assert "Questions To Ask" in intent_text, intent_text[:400] + + directions_text = (created / "reports" / "iteration-directions.md").read_text(encoding="utf-8") + assert "Top 3 Next Moves" in directions_text, directions_text[:400] + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_report_charts.py b/tests/verify_skill_report_charts.py new file mode 100644 index 0000000..7dd441a --- /dev/null +++ b/tests/verify_skill_report_charts.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPTS = ROOT / "scripts" +CLI = SCRIPTS / "yao.py" +sys.path.insert(0, str(SCRIPTS)) + + +def run(*args: str) -> dict: + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "payload": payload, + "stderr": proc.stderr, + } + + +def main() -> None: + from skill_report_charts import render_chart_set + from skill_report_model import build_report_model + + tmp_root = ROOT / "tests" / "tmp_skill_report_charts" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + result = run( + "init", + "chart-demo-skill", + "--description", + "Create structured campaign briefs from interviews, keyword notes, and content constraints.", + "--output-dir", + str(tmp_root), + ) + assert result["ok"], result + created = tmp_root / "chart-demo-skill" + + model = build_report_model(created) + charts = render_chart_set(model) + for key in ("radar", "flow", "matrix", "layers", "risk_heatmap", "asset_donut", "timeline"): + svg = charts.get(key, "") + assert svg.startswith("" in svg, (key, svg[:400]) + assert "
    " in svg, (key, svg[:400]) + assert 'data-lang="zh-CN"' in svg and 'data-lang="en"' in svg, (key, svg[:600]) + assert "http://" not in svg and "https://" not in svg, (key, svg[:400]) + assert "data:image" not in svg, (key, svg[:400]) + if key in {"flow", "layers"}: + assert 'fill="#F6F8FB"' in svg, (key, svg[:500]) + assert "Rating Radar" in charts["radar"], charts["radar"][:900] + assert "Delivery Flow" in charts["flow"], charts["flow"][:900] + assert "The radar chart compares" in charts["radar"], charts["radar"][-500:] + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skill_report_metrics.py b/tests/verify_skill_report_metrics.py new file mode 100644 index 0000000..7213ad1 --- /dev/null +++ b/tests/verify_skill_report_metrics.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPTS = ROOT / "scripts" +CLI = SCRIPTS / "yao.py" +sys.path.insert(0, str(SCRIPTS)) + + +def run(*args: str) -> dict: + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "payload": payload, + "stderr": proc.stderr, + } + + +def assert_metric(metric: dict) -> None: + assert isinstance(metric.get("score"), int), metric + assert 0 <= metric["score"] <= 100, metric + assert metric.get("label"), metric + assert metric.get("reasons"), metric + assert all(isinstance(reason, str) and reason for reason in metric["reasons"]), metric + + +def main() -> None: + from skill_report_metrics import calculate_scorecard + + tmp_root = ROOT / "tests" / "tmp_skill_report_metrics" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + + result = run( + "init", + "metric-demo-skill", + "--description", + "Turn customer research notes into a reusable strategy brief skill.", + "--output-dir", + str(tmp_root), + ) + assert result["ok"], result + created = tmp_root / "metric-demo-skill" + + scorecard = calculate_scorecard(created) + expected_keys = { + "completeness_score", + "trigger_score", + "evidence_score", + "maintainability_score", + "portability_score", + "context_cost", + } + assert expected_keys.issubset(scorecard.keys()), scorecard + for key in expected_keys: + assert_metric(scorecard[key]) + + assert scorecard["completeness_score"]["score"] >= 70, scorecard + assert scorecard["trigger_score"]["score"] >= 50, scorecard + assert any("SKILL.md" in reason for reason in scorecard["completeness_score"]["reasons"]), scorecard + + sparse_root = tmp_root / "sparse-skill" + sparse_root.mkdir() + (sparse_root / "SKILL.md").write_text( + "---\nname: sparse-skill\ndescription: Sparse demo.\n---\n\n# Sparse Skill\n", + encoding="utf-8", + ) + sparse_scorecard = calculate_scorecard(sparse_root) + assert sparse_scorecard["evidence_score"]["score"] < scorecard["evidence_score"]["score"], sparse_scorecard + assert any("证据不足" in reason for reason in sparse_scorecard["evidence_score"]["reasons"]), sparse_scorecard + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_skillops_opportunity.py b/tests/verify_skillops_opportunity.py new file mode 100644 index 0000000..a1ac9a0 --- /dev/null +++ b/tests/verify_skillops_opportunity.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +import json +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "scripts")) + +from skillops_opportunity import build_opportunities, decision_policy_contract, summarize_opportunities + + +def main() -> None: + patterns = [ + { + "pattern_id": "evidence_testing", + "support_count": 4, + "confidence": 0.95, + "evidence": [{"record_id": "a"}, {"record_id": "b"}, {"record_id": "c"}], + }, + { + "pattern_id": "approval_safety", + "support_count": 2, + "confidence": 0.79, + "evidence": [{"record_id": "d"}, {"record_id": "e"}], + }, + { + "pattern_id": "unknown_manual", + "support_count": 1, + "confidence": 0.55, + "evidence": [{"record_id": "f"}], + }, + ] + proposals = [ + { + "proposal_id": "adapt-high", + "pattern_id": "evidence_testing", + "title": "Attach tests and evidence refresh to each upgrade", + "risk_level": "medium", + "requires_approval": True, + "target_files": ["tests/verify_adaptation_safety.py"], + "verification_commands": ["python3 tests/verify_adaptation_safety.py"], + "evidence_refs": [{"record_id": "a"}, {"record_id": "b"}, {"record_id": "c"}], + }, + { + "proposal_id": "adapt-approval", + "pattern_id": "approval_safety", + "title": "Keep adaptive iteration approval-gated", + "risk_level": "low", + "requires_approval": True, + "target_files": ["AGENTS.md"], + "verification_commands": ["python3 tests/verify_adaptation_safety.py"], + "evidence_refs": [{"record_id": "d"}, {"record_id": "e"}], + }, + { + "proposal_id": "adapt-unknown", + "pattern_id": "unknown_manual", + "title": "Review repeated preference pattern", + "risk_level": "high", + "requires_approval": True, + "target_files": [], + "verification_commands": [], + "evidence_refs": [{"record_id": "f"}], + }, + ] + opportunities = build_opportunities(patterns, proposals) + assert len(opportunities) == 3, opportunities + assert opportunities[0]["score"] >= opportunities[1]["score"] >= opportunities[2]["score"], opportunities + by_pattern = {item["pattern_id"]: item for item in opportunities} + assert by_pattern["evidence_testing"]["action_type"] == "add_eval", by_pattern["evidence_testing"] + assert by_pattern["evidence_testing"]["decision"] in {"ready_for_approval_review", "proposal_review"}, by_pattern[ + "evidence_testing" + ] + assert by_pattern["approval_safety"]["action_type"] == "agents_update", by_pattern["approval_safety"] + assert by_pattern["unknown_manual"]["action_type"] == "report_only", by_pattern["unknown_manual"] + assert by_pattern["unknown_manual"]["score"] < by_pattern["evidence_testing"]["score"], by_pattern + assert all(item["write_allowed_without_approval"] is False for item in opportunities), opportunities + assert all(0 <= item["score"] <= 100 for item in opportunities), opportunities + summary = summarize_opportunities(opportunities) + assert summary["opportunity_count"] == 3, summary + assert summary["action_type_counts"]["add_eval"] == 1, summary + assert summary["action_type_counts"]["agents_update"] == 1, summary + assert summary["action_type_counts"]["report_only"] == 1, summary + assert summary["top_score"] == opportunities[0]["score"], summary + contract = decision_policy_contract() + assert contract["writes_source_files"] is False, contract + assert contract["auto_patch_enabled"] is False, contract + assert contract["approval_required_for_writes"] is True, contract + assert contract["score_range"] == [0, 100], contract + schema = json.loads((ROOT / "schemas" / "skillops-opportunity.schema.json").read_text(encoding="utf-8")) + assert "score" in schema["properties"], schema + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_system_model.py b/tests/verify_system_model.py new file mode 100644 index 0000000..3beeee2 --- /dev/null +++ b/tests/verify_system_model.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" + + +def run(*args: str) -> dict: + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stderr": proc.stderr, + } + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_system_model" + if tmp_root.exists(): + shutil.rmtree(tmp_root) + tmp_root.mkdir(parents=True, exist_ok=True) + + init_result = run( + "init", + "system-model-demo", + "--description", + "Turn repeated customer research notes into a reusable insight brief skill.", + "--output-dir", + str(tmp_root), + "--mode", + "production", + "--archetype", + "production", + "--intent-job", + "Turn repeated customer research notes into reusable insight briefs.", + "--intent-real-input", + "interview notes", + "--intent-real-input", + "support tickets", + "--intent-primary-output", + "a concise insight brief with risks and next actions", + "--intent-exclusion", + "do not invent customer quotes", + "--intent-constraint", + "protect private customer details", + "--intent-standard", + "specificity over generic summary", + ) + assert init_result["ok"], init_result + created = Path(init_result["payload"]["root"]) + assert (created / "reports" / "system-model.md").exists(), created + assert (created / "reports" / "system-model.json").exists(), created + assert init_result["payload"]["artifacts"]["system_model_md"].endswith("reports/system-model.md"), init_result + assert init_result["payload"]["system_model"]["stability"]["score"] >= 75, init_result + + payload = json.loads((created / "reports" / "system-model.json").read_text(encoding="utf-8")) + assert payload["boundary_map"]["owned_job"].startswith("Turn repeated customer research"), payload + assert "interview notes" in payload["boundary_map"]["input_boundary"], payload + assert payload["feedback_loops"], payload + assert payload["drift_watch"], payload + assert payload["failure_pattern_map"], payload + assert payload["leverage_points"], payload + + markdown = (created / "reports" / "system-model.md").read_text(encoding="utf-8") + assert "System Boundary Map" in markdown, markdown[:400] + assert "Feedback Loops" in markdown, markdown[:800] + assert "Delay And Drift Watch" in markdown, markdown[:1200] + assert "Highest Leverage Moves" in markdown, markdown[:1800] + + overview_html = (created / "reports" / "skill-overview.html").read_text(encoding="utf-8") + assert "原理结构" in overview_html, overview_html[:2400] + assert "质量评估" in overview_html, overview_html[:2600] + assert 'aria-label="Skill principle flow"' in overview_html, overview_html[:3000] + + rerender_result = run("system-model", str(created)) + assert rerender_result["ok"], rerender_result + assert rerender_result["payload"]["artifacts"]["markdown"].endswith("reports/system-model.md"), rerender_result + assert rerender_result["payload"]["summary"]["leverage_points"], rerender_result + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_telemetry_emit.py b/tests/verify_telemetry_emit.py new file mode 100644 index 0000000..bb03789 --- /dev/null +++ b/tests/verify_telemetry_emit.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_telemetry_emit" +EMITTER = ROOT / "scripts" / "emit_telemetry_event.py" +YAO = ROOT / "scripts" / "yao.py" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stdout": proc.stdout, + "stderr": proc.stderr, + } + + +def read_jsonl(path: Path) -> list[dict]: + if not path.exists(): + return [] + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "telemetry-emit-demo", + "--description", + "Emit metadata-only telemetry from an external client hook for later adoption drift import.", + "--output-dir", + str(TMP), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = TMP / "telemetry-emit-demo" + spool = TMP / "external-spool.jsonl" + + emitted = run( + [ + sys.executable, + str(EMITTER), + str(skill_dir), + "--output-jsonl", + str(spool), + "--event", + "skill_activation", + "--activation-type", + "explicit", + "--outcome", + "accepted", + "--failure-type", + "none", + "--command", + "browser-extension", + "--timestamp", + "2026-06-13T12:00:00Z", + ] + ) + assert emitted["ok"], emitted + assert emitted["payload"]["emitted"] is True, emitted + assert emitted["payload"]["artifacts"]["spool_jsonl"].endswith("external-spool.jsonl"), emitted + assert "telemetry-import" in emitted["payload"]["artifacts"]["import_command"], emitted + events = read_jsonl(spool) + assert len(events) == 1, events + assert events[0]["event"] == "skill_activation", events + assert events[0]["source"] == "external", events + assert events[0]["command"] == "browser-extension", events + + dry_run = run( + [ + sys.executable, + str(EMITTER), + str(skill_dir), + "--output-jsonl", + str(spool), + "--event", + "script_run", + "--outcome", + "failed", + "--failure-type", + "script_error", + "--command", + "browser-extension", + "--dry-run", + ] + ) + assert dry_run["ok"], dry_run + assert dry_run["payload"]["dry_run"] is True, dry_run + assert dry_run["payload"]["emitted"] is False, dry_run + assert len(read_jsonl(spool)) == 1, read_jsonl(spool) + + invalid = run( + [ + sys.executable, + str(EMITTER), + str(skill_dir), + "--output-jsonl", + str(spool), + "--command", + "bad/path", + ] + ) + assert invalid["returncode"] == 2, invalid + assert "command must use only" in "\n".join(invalid["payload"]["failures"]), invalid + assert len(read_jsonl(spool)) == 1, read_jsonl(spool) + + cli_emit = run( + [ + sys.executable, + str(YAO), + "telemetry-emit", + str(skill_dir), + "--output-jsonl", + str(spool), + "--event", + "skill_output", + "--activation-type", + "manual", + "--outcome", + "edited", + "--command", + "browser-plugin", + "--timestamp", + "2026-06-13T12:01:00Z", + ] + ) + assert cli_emit["ok"], cli_emit + assert cli_emit["payload"]["emitted"] is True, cli_emit + assert len(read_jsonl(spool)) == 2, read_jsonl(spool) + + imported = run( + [ + sys.executable, + str(YAO), + "telemetry-import", + str(skill_dir), + "--input-jsonl", + str(spool), + "--generated-at", + "2026-06-13T12:02:00Z", + ] + ) + assert imported["ok"], imported + assert imported["payload"]["imported_count"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["event_count"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["source_types"]["external"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["command_counts"]["browser-extension"] == 1, imported + assert imported["payload"]["adoption_drift"]["summary"]["command_counts"]["browser-plugin"] == 1, imported + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_telemetry_hooks.py b/tests/verify_telemetry_hooks.py new file mode 100644 index 0000000..9ad98f7 --- /dev/null +++ b/tests/verify_telemetry_hooks.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_telemetry_hooks" +HOOKS = ROOT / "scripts" / "render_telemetry_hook_recipes.py" +YAO = ROOT / "scripts" / "yao.py" +SENSITIVE_FLAGS = { + "--prompt", + "--content", + "--input", + "--inputs", + "--output", + "--outputs", + "--transcript", + "--message", + "--messages", + "--note", + "--raw", + "--text", +} + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stdout": proc.stdout, + "stderr": proc.stderr, + } + + +def read_jsonl(path: Path) -> list[dict]: + if not path.exists(): + return [] + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + + +def assert_metadata_only(argv: list[str]) -> None: + lowered = {item.lower() for item in argv} + assert not (lowered & SENSITIVE_FLAGS), argv + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "telemetry-hooks-demo", + "--description", + "Render local telemetry client hook recipes without collecting raw content.", + "--output-dir", + str(TMP), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = TMP / "telemetry-hooks-demo" + output_json = TMP / "telemetry_hook_recipes.json" + output_md = TMP / "telemetry_hook_recipes.md" + spool = TMP / "client-spool.jsonl" + rendered = run( + [ + sys.executable, + str(HOOKS), + str(skill_dir), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--output-jsonl", + str(spool), + ] + ) + assert rendered["ok"], rendered + payload = rendered["payload"] + assert payload["summary"]["recipe_count"] == 5, payload + assert payload["summary"]["native_auto_capture_count"] == 0, payload + assert payload["summary"]["metadata_only_recipe_count"] == 5, payload + assert payload["privacy_contract"]["raw_content_allowed"] is False, payload + assert {"browser-extension", "chrome-extension", "vscode-extension", "cli-wrapper", "provider-adapter"} == { + item["id"] for item in payload["recipes"] + }, payload + assert output_json.exists(), output_json + assert output_md.exists(), output_md + markdown = output_md.read_text(encoding="utf-8") + assert "Telemetry Hook Recipes" in markdown, markdown + assert "not proof that a host client is already natively integrated" in markdown, markdown + + for recipe in payload["recipes"]: + assert recipe["metadata_only"] is True, recipe + assert recipe["source"] == "external", recipe + assert recipe["integration_status"] == "client-hook-recipe", recipe + assert recipe["emit_argv"][:3] == ["python3", "scripts/yao.py", "telemetry-emit"], recipe + assert recipe["dry_run_argv"][-1] == "--dry-run", recipe + assert "--command" in recipe["emit_argv"], recipe + assert_metadata_only(recipe["emit_argv"]) + assert_metadata_only(recipe["dry_run_argv"]) + dry_run = run(recipe["dry_run_argv"]) + assert dry_run["ok"], dry_run + assert dry_run["payload"]["dry_run"] is True, dry_run + assert dry_run["payload"]["emitted"] is False, dry_run + assert read_jsonl(spool) == [], spool + + browser = next(item for item in payload["recipes"] if item["id"] == "browser-extension") + emitted = run(browser["emit_argv"]) + assert emitted["ok"], emitted + assert emitted["payload"]["emitted"] is True, emitted + events = read_jsonl(spool) + assert len(events) == 1, events + assert events[0]["command"] == "browser-extension", events + assert events[0]["event"] == "skill_activation", events + + imported = run(payload["artifacts"]["import_argv"]) + assert imported["ok"], imported + assert imported["payload"]["imported_count"] == 1, imported + assert imported["payload"]["adoption_drift"]["summary"]["source_types"]["external"] == 1, imported + assert imported["payload"]["adoption_drift"]["summary"]["command_counts"]["browser-extension"] == 1, imported + + cli_output_json = TMP / "cli_telemetry_hook_recipes.json" + cli = run( + [ + sys.executable, + str(YAO), + "telemetry-hooks", + str(skill_dir), + "--output-json", + str(cli_output_json), + "--output-jsonl", + str(spool), + ] + ) + assert cli["ok"], cli + assert cli["payload"]["summary"]["recipe_count"] == 5, cli + assert cli_output_json.exists(), cli_output_json + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_telemetry_import.py b/tests/verify_telemetry_import.py new file mode 100644 index 0000000..9c9dfeb --- /dev/null +++ b/tests/verify_telemetry_import.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_telemetry_import" +IMPORTER = ROOT / "scripts" / "import_telemetry_events.py" +YAO = ROOT / "scripts" / "yao.py" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stdout": proc.stdout, + "stderr": proc.stderr, + } + + +def write_jsonl(path: Path, rows: list[dict]) -> None: + path.write_text("\n".join(json.dumps(row, ensure_ascii=False) for row in rows) + "\n", encoding="utf-8") + + +def main() -> None: + if TMP.exists(): + shutil.rmtree(TMP) + TMP.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "external-telemetry-demo", + "--description", + "Import external client telemetry into a reusable metadata-only drift loop.", + "--output-dir", + str(TMP), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = TMP / "external-telemetry-demo" + input_jsonl = TMP / "external-events.jsonl" + write_jsonl( + input_jsonl, + [ + { + "event": "skill_activation", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "timestamp": "2026-06-13T11:00:00Z", + }, + { + "event": "script_run", + "activation_type": "manual", + "outcome": "failed", + "failure_type": "script_error", + "timestamp": "2026-06-13T11:01:00Z", + }, + ], + ) + + dry_run = run( + [ + sys.executable, + str(IMPORTER), + str(skill_dir), + "--input-jsonl", + str(input_jsonl), + "--command", + "chrome-extension", + "--dry-run", + ] + ) + assert dry_run["ok"], dry_run + assert dry_run["payload"]["dry_run"] is True, dry_run + assert dry_run["payload"]["candidate_count"] == 2, dry_run + assert dry_run["payload"]["imported_count"] == 0, dry_run + assert not (skill_dir / "reports" / "telemetry_events.jsonl").exists(), dry_run + + imported = run( + [ + sys.executable, + str(IMPORTER), + str(skill_dir), + "--input-jsonl", + str(input_jsonl), + "--command", + "chrome-extension", + "--generated-at", + "2026-06-13T11:02:00Z", + ] + ) + assert imported["ok"], imported + assert imported["payload"]["imported_count"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["event_count"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["source_types"]["external"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["command_counts"]["chrome-extension"] == 2, imported + events = [ + json.loads(line) + for line in (skill_dir / "reports" / "telemetry_events.jsonl").read_text(encoding="utf-8").splitlines() + ] + assert len(events) == 2, events + assert {event["source"] for event in events} == {"external"}, events + assert {event["command"] for event in events} == {"chrome-extension"}, events + markdown = (skill_dir / "reports" / "adoption_drift_report.md").read_text(encoding="utf-8") + assert "source=`external` command=`chrome-extension`" in markdown, markdown + + unsafe_jsonl = TMP / "unsafe-events.jsonl" + write_jsonl( + unsafe_jsonl, + [ + { + "event": "skill_activation", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "timestamp": "2026-06-13T11:03:00Z", + }, + { + "event": "skill_activation", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "prompt": "raw user prompt must never be imported", + "timestamp": "2026-06-13T11:04:00Z", + }, + ], + ) + unsafe = run([sys.executable, str(IMPORTER), str(skill_dir), "--input-jsonl", str(unsafe_jsonl)]) + assert unsafe["returncode"] == 2, unsafe + assert not unsafe["payload"]["ok"], unsafe + assert "raw content fields" in "\n".join(unsafe["payload"]["failures"]), unsafe + events_after_failure = (skill_dir / "reports" / "telemetry_events.jsonl").read_text(encoding="utf-8").splitlines() + assert len(events_after_failure) == 2, events_after_failure + + cli_input_jsonl = TMP / "cli-external-events.jsonl" + write_jsonl( + cli_input_jsonl, + [ + { + "event": "skill_output", + "activation_type": "manual", + "outcome": "edited", + "failure_type": "none", + "timestamp": "2026-06-13T11:05:00Z", + } + ], + ) + cli = run( + [ + sys.executable, + str(YAO), + "telemetry-import", + str(skill_dir), + "--input-jsonl", + str(cli_input_jsonl), + "--command", + "browser-plugin", + "--generated-at", + "2026-06-13T11:06:00Z", + ] + ) + assert cli["ok"], cli + assert cli["payload"]["imported_count"] == 1, cli + assert cli["payload"]["adoption_drift"]["summary"]["event_count"] == 3, cli + assert cli["payload"]["adoption_drift"]["summary"]["source_types"]["external"] == 3, cli + assert cli["payload"]["adoption_drift"]["summary"]["command_counts"]["browser-plugin"] == 1, cli + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_telemetry_native_host.py b/tests/verify_telemetry_native_host.py new file mode 100644 index 0000000..b1443e1 --- /dev/null +++ b/tests/verify_telemetry_native_host.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +import json +import shutil +import struct +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_telemetry_native_host" +HOST = ROOT / "scripts" / "telemetry_native_host.py" +YAO = ROOT / "scripts" / "yao.py" +ORIGIN = "chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = json.loads(proc.stdout) if proc.stdout.strip() else {} + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stdout": proc.stdout, + "stderr": proc.stderr, + } + + +def pack_message(payload: dict) -> bytes: + data = json.dumps(payload, ensure_ascii=False).encode("utf-8") + return struct.pack(" list[dict]: + messages = [] + offset = 0 + while offset < len(data): + size = struct.unpack(" list[dict]: + if not path.exists(): + return [] + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + subprocess.run( + [ + sys.executable, + str(ROOT / "scripts" / "init_skill.py"), + "telemetry-native-host-demo", + "--description", + "Receive metadata-only telemetry events from a Browser native messaging host.", + "--output-dir", + str(TMP), + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + skill_dir = TMP / "telemetry-native-host-demo" + spool = TMP / "native-spool.jsonl" + + valid = run( + [ + sys.executable, + str(HOST), + str(skill_dir), + "--output-jsonl", + str(spool), + "--message-json", + json.dumps( + { + "event": "skill_activation", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "command": "chrome-native-host", + } + ), + ] + ) + assert valid["ok"], valid + assert valid["payload"]["emitted"] is True, valid + events = read_jsonl(spool) + assert len(events) == 1, events + assert events[0]["command"] == "chrome-native-host", events + assert events[0]["source"] == "external", events + + invalid = run( + [ + sys.executable, + str(HOST), + str(skill_dir), + "--output-jsonl", + str(spool), + "--message-json", + json.dumps( + { + "event": "skill_activation", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "command": "chrome-native-host", + "prompt": "raw prompt must not pass through native host", + } + ), + ] + ) + assert invalid["returncode"] == 2, invalid + assert "raw content fields" in "\n".join(invalid["payload"]["failures"]), invalid + assert len(read_jsonl(spool)) == 1, read_jsonl(spool) + + stdio_payload = pack_message( + { + "event": "skill_output", + "activation_type": "manual", + "outcome": "edited", + "failure_type": "none", + "command": "browser-native-host", + } + ) + stdio = subprocess.run( + [ + sys.executable, + str(HOST), + str(skill_dir), + "--output-jsonl", + str(spool), + "--stdio", + ], + cwd=ROOT, + input=stdio_payload, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + assert stdio.returncode == 0, stdio.stderr + responses = unpack_messages(stdio.stdout) + assert len(responses) == 1, responses + assert responses[0]["ok"], responses + assert responses[0]["emitted"] is True, responses + assert len(read_jsonl(spool)) == 2, read_jsonl(spool) + + manifest = TMP / "native-host-manifest.json" + launcher = TMP / "yao-telemetry-native-host.sh" + manifest_result = run( + [ + sys.executable, + str(HOST), + str(skill_dir), + "--output-jsonl", + str(spool), + "--write-launcher", + str(launcher), + "--write-manifest", + str(manifest), + "--allowed-origin", + ORIGIN, + ] + ) + assert manifest_result["ok"], manifest_result + assert manifest.exists(), manifest + assert launcher.exists(), launcher + assert launcher.stat().st_mode & 0o111, launcher.stat().st_mode + manifest_payload = json.loads(manifest.read_text(encoding="utf-8")) + assert manifest_payload["name"] == "com.yao.meta_skill.telemetry", manifest_payload + assert manifest_payload["type"] == "stdio", manifest_payload + assert manifest_payload["path"] == str(launcher.resolve()), manifest_payload + assert manifest_payload["allowed_origins"] == [ORIGIN], manifest_payload + + manifest_without_launcher = TMP / "native-host-manifest-without-launcher.json" + manifest_without_launcher_result = run( + [ + sys.executable, + str(HOST), + str(skill_dir), + "--write-manifest", + str(manifest_without_launcher), + "--allowed-origin", + ORIGIN, + ] + ) + assert manifest_without_launcher_result["returncode"] == 2, manifest_without_launcher_result + assert not manifest_without_launcher.exists(), manifest_without_launcher + assert "must be executable" in "\n".join(manifest_without_launcher_result["payload"]["failures"]) + + imported = run( + [ + sys.executable, + str(YAO), + "telemetry-import", + str(skill_dir), + "--input-jsonl", + str(spool), + "--generated-at", + "2026-06-13T12:30:00Z", + ] + ) + assert imported["ok"], imported + assert imported["payload"]["imported_count"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["source_types"]["external"] == 2, imported + assert imported["payload"]["adoption_drift"]["summary"]["command_counts"]["chrome-native-host"] == 1, imported + assert imported["payload"]["adoption_drift"]["summary"]["command_counts"]["browser-native-host"] == 1, imported + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_trust_check.py b/tests/verify_trust_check.py new file mode 100644 index 0000000..10ff49d --- /dev/null +++ b/tests/verify_trust_check.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "trust_check.py" +sys.path.insert(0, str(ROOT / "scripts")) + +from trust_check import iter_scan_files, relpath + + +INTERFACE = """interface: + display_name: "Trust Demo" + short_description: "Trust check demo" + default_prompt: "Use trust demo." +compatibility: + canonical_format: "agent-skills" + adapter_targets: + - "generic" + activation: + mode: "manual" + paths: [] + execution: + context: "inline" + shell: "bash" + trust: + source_tier: "local" + remote_inline_execution: "forbid" + remote_metadata_policy: "allow-metadata-only" + degradation: + generic: "neutral-source" +""" + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_trust" + shutil.rmtree(tmp_root, ignore_errors=True) + tmp_root.mkdir(parents=True, exist_ok=True) + output_json = tmp_root / "security_trust_report.json" + output_md = tmp_root / "security_trust_report.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["ok"], payload + assert payload["summary"]["secret_findings"] == 0, payload + assert payload["summary"]["package_hash_scope"] == "source-contract-without-generated-reports", payload + assert payload["summary"]["package_hash_file_count"] == payload["summary"]["scanned_files"], payload + assert payload["summary"]["package_sha256"], payload + scanned_paths = {relpath(ROOT, path) for path in iter_scan_files(ROOT)} + assert "assets/skill-overview.css" in scanned_paths, scanned_paths + assert "assets/skill-overview.js" in scanned_paths, scanned_paths + assert "assets/review-studio.css" in scanned_paths, scanned_paths + assert "assets/review-viewer.css" in scanned_paths, scanned_paths + assert payload["summary"]["internal_module_count"] >= 3, payload + assert payload["summary"]["network_script_count"] == 3, payload + assert payload["summary"]["network_policy_covered_count"] == payload["summary"]["network_script_count"], payload + assert payload["summary"]["network_policy_missing_count"] == 0, payload + assert payload["summary"]["permission_required_count"] >= 3, payload + assert payload["summary"]["permission_approved_count"] == payload["summary"]["permission_required_count"], payload + assert payload["summary"]["permission_missing_count"] == 0, payload + assert payload["summary"]["permission_invalid_count"] == 0, payload + assert payload["summary"]["permission_expired_count"] == 0, payload + assert payload["summary"]["help_smoke_checked_count"] > 0, payload + assert payload["summary"]["help_smoke_failed_count"] == 0, payload + assert payload["help_smoke"]["enabled"], payload["help_smoke"] + assert payload["help_smoke"]["checked_count"] == payload["help_smoke"]["passed_count"], payload["help_smoke"] + assert payload["help_smoke"]["failed_scripts"] == [], payload["help_smoke"] + assert payload["help_smoke"]["skipped_count"] == payload["summary"]["internal_module_count"], payload["help_smoke"] + assert payload["network_policy"]["present"], payload["network_policy"] + assert payload["network_policy"]["missing_scripts"] == [], payload["network_policy"] + assert payload["network_policy"]["mismatches"] == [], payload["network_policy"] + assert payload["permission_governance"]["present"], payload["permission_governance"] + assert {"network", "file_write", "subprocess"} <= set(payload["permission_governance"]["approved_capabilities"]), payload["permission_governance"] + assert payload["permission_governance"]["missing_capabilities"] == [], payload["permission_governance"] + assert payload["permission_governance"]["invalid_capabilities"] == [], payload["permission_governance"] + assert payload["permission_governance"]["expired_capabilities"] == [], payload["permission_governance"] + assert "scripts/check_update.py" in payload["network_policy"]["covered_scripts"], payload["network_policy"] + assert "scripts/github_benchmark_scan.py" in payload["network_policy"]["covered_scripts"], payload["network_policy"] + assert "scripts/provider_output_eval_runner.py" in payload["network_policy"]["covered_scripts"], payload["network_policy"] + script_map = {item["path"]: item for item in payload["scripts"]} + for internal_module in [ + "scripts/build_skill_atlas_layout.py", + "scripts/build_skill_atlas_opportunities.py", + "scripts/compile_skill_targets.py", + "scripts/cross_packager_contracts.py", + "scripts/description_optimizer_reporting.py", + "scripts/reference_synthesis_markdown.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_gate_contract.py", + "scripts/review_studio_gate_helpers.py", + "scripts/review_studio_gates.py", + "scripts/review_studio_layout.py", + "scripts/review_studio_panels.py", + "scripts/trust_check_scripts.py", + "scripts/evidence_consistency_core.py", + "scripts/evidence_consistency_release.py", + "scripts/evidence_consistency_skill_os2_review.py", + "scripts/evidence_consistency_world_class.py", + "scripts/skill_ir_paths.py", + "scripts/skill_report_charts.py", + "scripts/skill_report_i18n.py", + "scripts/skill_report_layout.py", + "scripts/skill_report_metrics.py", + "scripts/skill_report_model.py", + "scripts/skill_report_sections.py", + "scripts/skill_report_sources.py", + "scripts/skill_report_world_class.py", + "scripts/skill_os2_coverage_markdown.py", + "scripts/world_class_preflight_layout.py", + "scripts/world_class_evidence_contract.py", + "scripts/world_class_source_checks.py", + "scripts/yao_cli_adaptation_commands.py", + "scripts/yao_cli_config.py", + "scripts/yao_cli_distribution_commands.py", + "scripts/yao_cli_output_commands.py", + "scripts/yao_cli_operator_commands.py", + "scripts/yao_cli_parser.py", + "scripts/yao_cli_parser_evidence.py", + "scripts/yao_cli_parser_operator.py", + "scripts/yao_cli_parser_operations.py", + "scripts/yao_cli_report_commands.py", + "scripts/yao_cli_telemetry.py", + ]: + assert script_map[internal_module]["interface"] == "internal-module", script_map[internal_module] + assert script_map[internal_module]["interface_declared"], script_map[internal_module] + warning_text = "\n".join(payload["warnings"]) + assert "build_skill_atlas_layout.py" not in warning_text, payload["warnings"] + assert "build_skill_atlas_opportunities.py" not in warning_text, payload["warnings"] + assert "compile_skill_targets.py" not in warning_text, payload["warnings"] + assert "cross_packager_contracts.py" not in warning_text, payload["warnings"] + assert "description_optimizer_reporting.py" not in warning_text, payload["warnings"] + assert "reference_synthesis_markdown.py" not in warning_text, payload["warnings"] + assert "review_studio_formatting.py" not in warning_text, payload["warnings"] + assert "review_studio_gate_contract.py" not in warning_text, payload["warnings"] + assert "review_studio_gate_helpers.py" not in warning_text, payload["warnings"] + assert "review_studio_gates.py" not in warning_text, payload["warnings"] + assert "review_studio_layout.py" not in warning_text, payload["warnings"] + assert "review_studio_panels.py" not in warning_text, payload["warnings"] + assert "trust_check_scripts.py" not in warning_text, payload["warnings"] + assert "evidence_consistency_core.py" not in warning_text, payload["warnings"] + assert "evidence_consistency_release.py" not in warning_text, payload["warnings"] + assert "evidence_consistency_skill_os2_review.py" not in warning_text, payload["warnings"] + assert "evidence_consistency_world_class.py" not in warning_text, payload["warnings"] + assert "skill_ir_paths.py" not in warning_text, payload["warnings"] + assert "skill_report_charts.py" not in warning_text, payload["warnings"] + assert "skill_report_i18n.py" not in warning_text, payload["warnings"] + assert "skill_report_layout.py" not in warning_text, payload["warnings"] + assert "skill_report_metrics.py" not in warning_text, payload["warnings"] + assert "skill_report_model.py" not in warning_text, payload["warnings"] + assert "skill_report_sections.py" not in warning_text, payload["warnings"] + assert "skill_report_sources.py" not in warning_text, payload["warnings"] + assert "skill_report_world_class.py" not in warning_text, payload["warnings"] + assert "skill_os2_coverage_markdown.py" not in warning_text, payload["warnings"] + assert "world_class_preflight_layout.py" not in warning_text, payload["warnings"] + assert "world_class_evidence_contract.py" not in warning_text, payload["warnings"] + assert "world_class_source_checks.py" not in warning_text, payload["warnings"] + assert "yao_cli_adaptation_commands.py" not in warning_text, payload["warnings"] + assert "yao_cli_config.py" not in warning_text, payload["warnings"] + assert "yao_cli_distribution_commands.py" not in warning_text, payload["warnings"] + assert "yao_cli_output_commands.py" not in warning_text, payload["warnings"] + assert "yao_cli_operator_commands.py" not in warning_text, payload["warnings"] + assert "yao_cli_parser.py" not in warning_text, payload["warnings"] + assert "yao_cli_parser_evidence.py" not in warning_text, payload["warnings"] + assert "yao_cli_parser_operator.py" not in warning_text, payload["warnings"] + assert "yao_cli_parser_operations.py" not in warning_text, payload["warnings"] + assert "yao_cli_report_commands.py" not in warning_text, payload["warnings"] + assert "yao_cli_telemetry.py" not in warning_text, payload["warnings"] + assert "render_context_reports.py" not in warning_text, payload["warnings"] + assert "render_social_preview.py" not in warning_text, payload["warnings"] + assert "Network-capable scripts require bounded host policy" not in warning_text, payload["warnings"] + assert "CLI help smoke failed" not in warning_text, payload["warnings"] + assert "Permission approvals" not in warning_text, payload["warnings"] + assert output_json.exists(), output_json + assert output_md.exists(), output_md + assert "Security Trust Report" in output_md.read_text(encoding="utf-8") + assert "Package hash scope" in output_md.read_text(encoding="utf-8") + + secret_skill = tmp_root / "secret-skill" + (secret_skill / "agents").mkdir(parents=True, exist_ok=True) + (secret_skill / "scripts").mkdir(parents=True, exist_ok=True) + (secret_skill / "SKILL.md").write_text( + "---\nname: secret-skill\ndescription: Secret demo.\n---\n\n# Secret\n", + encoding="utf-8", + ) + (secret_skill / "agents" / "interface.yaml").write_text(INTERFACE, encoding="utf-8") + (secret_skill / "scripts" / "leaky.py").write_text( + "TOKEN = 'ghp_1234567890abcdefghijklmnopqrstuv'\n", + encoding="utf-8", + ) + secret_proc = subprocess.run( + [sys.executable, str(SCRIPT), str(secret_skill)], + cwd=ROOT, + capture_output=True, + text=True, + ) + assert secret_proc.returncode == 2, secret_proc.stdout + secret_payload = json.loads(secret_proc.stdout) + assert not secret_payload["ok"], secret_payload + assert secret_payload["summary"]["secret_findings"] == 1, secret_payload + assert secret_payload["secrets"][0]["type"] == "github_token", secret_payload + + network_skill = tmp_root / "network-skill" + (network_skill / "agents").mkdir(parents=True, exist_ok=True) + (network_skill / "scripts").mkdir(parents=True, exist_ok=True) + (network_skill / "SKILL.md").write_text( + "---\nname: network-skill\ndescription: Network demo.\n---\n\n# Network\n", + encoding="utf-8", + ) + (network_skill / "agents" / "interface.yaml").write_text(INTERFACE, encoding="utf-8") + (network_skill / "scripts" / "probe.py").write_text( + "from urllib.request import urlopen\n\nurlopen('https://example.com/status')\n", + encoding="utf-8", + ) + missing_policy_proc = subprocess.run( + [sys.executable, str(SCRIPT), str(network_skill)], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + missing_policy_payload = json.loads(missing_policy_proc.stdout) + assert missing_policy_payload["summary"]["network_script_count"] == 1, missing_policy_payload + assert missing_policy_payload["summary"]["network_policy_missing_count"] == 1, missing_policy_payload + assert any("Network-capable scripts require bounded host policy" in item for item in missing_policy_payload["warnings"]), missing_policy_payload + + (network_skill / "security").mkdir(parents=True, exist_ok=True) + (network_skill / "security" / "network_policy.json").write_text( + json.dumps( + { + "schema_version": "1.0", + "scripts": { + "scripts/probe.py": { + "purpose": "Test policy coverage.", + "allowed_hosts": ["example.com"], + "allowed_path_prefixes": ["/"], + "requires_https": True, + "custom_url_policy": "not supported", + } + }, + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + covered_policy_proc = subprocess.run( + [sys.executable, str(SCRIPT), str(network_skill)], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + covered_policy_payload = json.loads(covered_policy_proc.stdout) + assert covered_policy_payload["summary"]["network_policy_covered_count"] == 1, covered_policy_payload + assert covered_policy_payload["summary"]["network_policy_missing_count"] == 0, covered_policy_payload + assert not any("Network-capable scripts require bounded host policy" in item for item in covered_policy_payload["warnings"]), covered_policy_payload + assert covered_policy_payload["summary"]["permission_missing_count"] == 1, covered_policy_payload + assert covered_policy_payload["permission_governance"]["missing_capabilities"] == ["network"], covered_policy_payload + assert any("Permission approvals missing: network" in item for item in covered_policy_payload["warnings"]), covered_policy_payload + + (network_skill / "security" / "permission_policy.json").write_text( + json.dumps( + { + "schema_version": "1.0", + "capabilities": { + "network": { + "decision": "approved", + "reviewer": "Yao Team", + "scope": "Test network probe.", + "reason": "Network access is bounded to example.com for this isolated test fixture.", + "expires_at": "2026-09-30", + "target_enforcement": { + "openai": "metadata-only", + "claude": "adapter metadata", + "generic": "adapter metadata", + "vscode": "workspace trust note", + }, + } + }, + }, + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + approved_policy_proc = subprocess.run( + [sys.executable, str(SCRIPT), str(network_skill)], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + approved_policy_payload = json.loads(approved_policy_proc.stdout) + assert approved_policy_payload["summary"]["permission_approved_count"] == 1, approved_policy_payload + assert approved_policy_payload["summary"]["permission_missing_count"] == 0, approved_policy_payload + assert not any("Permission approvals missing" in item for item in approved_policy_payload["warnings"]), approved_policy_payload + + help_smoke_skill = tmp_root / "help-smoke-skill" + (help_smoke_skill / "agents").mkdir(parents=True, exist_ok=True) + (help_smoke_skill / "scripts").mkdir(parents=True, exist_ok=True) + (help_smoke_skill / "SKILL.md").write_text( + "---\nname: help-smoke-skill\ndescription: Help smoke demo.\n---\n\n# Help Smoke\n", + encoding="utf-8", + ) + (help_smoke_skill / "agents" / "interface.yaml").write_text(INTERFACE, encoding="utf-8") + (help_smoke_skill / "scripts" / "bad_help.py").write_text( + "import argparse\n\nraise RuntimeError('help import failed')\n", + encoding="utf-8", + ) + help_smoke_proc = subprocess.run( + [sys.executable, str(SCRIPT), str(help_smoke_skill)], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + help_smoke_payload = json.loads(help_smoke_proc.stdout) + assert help_smoke_payload["summary"]["help_smoke_checked_count"] == 1, help_smoke_payload + assert help_smoke_payload["summary"]["help_smoke_failed_count"] == 1, help_smoke_payload + assert help_smoke_payload["help_smoke"]["failed_scripts"] == ["scripts/bad_help.py"], help_smoke_payload + assert any("CLI help smoke failed: scripts/bad_help.py" in item for item in help_smoke_payload["warnings"]), help_smoke_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_upgrade_check.py b/tests/verify_upgrade_check.py new file mode 100644 index 0000000..cbe2138 --- /dev/null +++ b/tests/verify_upgrade_check.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "upgrade_check.py" +PREVIOUS = ROOT / "registry" / "examples" / "yao-meta-skill-1.0.0.json" + + +def run(cmd: list[str]) -> dict: + proc = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True) + payload = {} + if proc.stdout.strip(): + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + "payload": payload, + } + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_upgrade_check" + tmp_root.mkdir(parents=True, exist_ok=True) + output_json = tmp_root / "upgrade_check.json" + output_md = tmp_root / "upgrade_check.md" + result = run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--previous-package-json", + str(PREVIOUS), + "--current-package-json", + str(ROOT / "reports" / "registry_audit.json"), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-13", + ] + ) + payload = result["payload"] + assert result["ok"], result + assert payload["ok"], payload + assert payload["summary"]["declared_bump"] == "minor", payload + assert payload["summary"]["recommended_bump"] == "minor", payload + assert "agent-skills-compatible" in payload["upgrade_diff"]["added_targets"], payload + assert "vscode" in payload["upgrade_diff"]["added_targets"], payload + assert output_json.exists(), output_json + assert "Upgrade Check" in output_md.read_text(encoding="utf-8"), output_md + + previous = json.loads(PREVIOUS.read_text(encoding="utf-8")) + current = json.loads((ROOT / "reports" / "registry_audit.json").read_text(encoding="utf-8"))["package"] + current["version"] = "1.1.0" + current["targets"] = ["openai", "claude"] + current["compatibility"] = {"openai": "pass", "claude": "fail"} + bad_current = tmp_root / "bad-current.json" + bad_current.write_text(json.dumps(current, ensure_ascii=False, indent=2), encoding="utf-8") + bad = run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--previous-package-json", + str(PREVIOUS), + "--current-package-json", + str(bad_current), + "--output-json", + str(tmp_root / "bad.json"), + "--output-md", + str(tmp_root / "bad.md"), + "--generated-at", + "2026-06-13", + ] + ) + bad_payload = bad["payload"] + assert bad["returncode"] == 2, bad + assert not bad_payload["ok"], bad_payload + assert bad_payload["summary"]["recommended_bump"] == "major", bad_payload + assert any("Version bump is insufficient" in item for item in bad_payload["failures"]), bad_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_weekly_curator.py b/tests/verify_weekly_curator.py new file mode 100644 index 0000000..a745326 --- /dev/null +++ b/tests/verify_weekly_curator.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_weekly_curator_report.py" +CLI = ROOT / "scripts" / "yao.py" +TMP = ROOT / "tests" / "tmp_weekly_curator" + + +def run_command(*args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, *args], + cwd=ROOT, + capture_output=True, + text=True, + ) + + +def write_json(path: Path, payload: dict) -> 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 seed_reports(skill_dir: Path) -> None: + reports = skill_dir / "reports" + write_json( + reports / "skillops" / "daily" / "2026-06-16.json", + { + "ok": True, + "generated_at": "2026-06-16T08:00:00Z", + "opportunity_summary": {"opportunity_count": 3}, + "opportunities": [ + { + "opportunity_id": "skillops-aaaaaaaaaa", + "proposal_id": "adapt-a", + "pattern_id": "report_ui", + "title": "Improve report layout", + "opportunity_type": "artifact-quality", + "action_type": "patch_existing_skill", + "decision": "ready_for_approval_review", + "priority": "high", + "score": 92, + "risk_level": "medium", + "requires_approval": True, + "write_allowed_without_approval": False, + "evidence_count": 5, + "target_files": ["scripts/render_skill_overview.py"], + "verification_commands": ["python3 tests/verify_skill_overview.py"], + "policy_reason": "Report layout feedback maps to renderer changes.", + }, + { + "opportunity_id": "skillops-bbbbbbbbbb", + "proposal_id": "adapt-b", + "pattern_id": "approval_safety", + "title": "Keep adaptive changes approval-gated", + "opportunity_type": "governance", + "action_type": "agents_update", + "decision": "proposal_review", + "priority": "medium", + "score": 78, + "risk_level": "low", + "requires_approval": True, + "write_allowed_without_approval": False, + "evidence_count": 2, + "target_files": ["AGENTS.md"], + "verification_commands": ["python3 tests/verify_adaptation_safety.py"], + "policy_reason": "Governance feedback maps to durable guidance.", + }, + { + "opportunity_id": "skillops-cccccccccc", + "proposal_id": "adapt-c", + "pattern_id": "evidence_testing", + "title": "Add evidence regression", + "opportunity_type": "quality-gate", + "action_type": "add_eval", + "decision": "proposal_review", + "priority": "medium", + "score": 72, + "risk_level": "medium", + "requires_approval": True, + "write_allowed_without_approval": False, + "evidence_count": 2, + "target_files": ["tests/verify_daily_skillops.py"], + "verification_commands": ["python3 tests/verify_daily_skillops.py"], + "policy_reason": "Verification feedback maps to focused checks.", + }, + ], + }, + ) + write_json( + reports / "skill_atlas.json", + { + "ok": True, + "summary": { + "skill_count": 4, + "route_collision_count": 2, + "actionable_route_collision_count": 1, + "owner_gap_count": 1, + "actionable_owner_gap_count": 0, + "stale_count": 1, + "actionable_stale_count": 0, + "drift_signal_count": 1, + "actionable_drift_signal_count": 0, + "no_route_opportunity_count": 1, + }, + }, + ) + write_json(reports / "benchmark_reproducibility.json", {"ok": True, "summary": {"release_lock_ready": True}}) + write_json(reports / "evidence_consistency.json", {"ok": True, "summary": {"decision": "consistent"}}) + write_json( + reports / "world_class_evidence_ledger.json", + {"ok": True, "summary": {"pending_count": 4, "ready_to_claim_world_class": False}}, + ) + + +def assert_contract(payload: dict) -> None: + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + assert payload["report_contract"]["contract"] == "weekly-skillops-curator-report", payload + assert payload["curator_contract"]["writes_source_files"] is False, payload + assert payload["curator_contract"]["auto_patch_enabled"] is False, payload + assert payload["curator_contract"]["approval_required_for_writes"] is True, payload + assert payload["curator_contract"]["counts_as_world_class_evidence"] is False, payload + for field in payload["report_contract"]["summary_fields"]: + assert payload[field] == payload["summary"][field], field + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + skill_dir = TMP / "weekly-curator-demo" + skill_dir.mkdir(parents=True) + seed_reports(skill_dir) + output_json = skill_dir / "reports" / "skillops" / "weekly" / "2026-W25.json" + output_md = skill_dir / "reports" / "skillops" / "weekly" / "2026-W25.md" + + proc = run_command( + str(SCRIPT), + str(skill_dir), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-16T08:00:00Z", + ) + assert proc.returncode == 0, proc.stderr + payload = json.loads(proc.stdout) + assert_contract(payload) + assert payload["summary"]["week_id"] == "2026-W25", payload + assert payload["summary"]["daily_report_count"] == 1, payload + assert payload["summary"]["unique_opportunity_count"] == 3, payload + assert payload["summary"]["ready_for_approval_review_count"] == 1, payload + assert payload["summary"]["proposal_review_count"] == 2, payload + assert payload["summary"]["top_score"] == 92, payload + assert payload["summary"]["skill_count"] == 4, payload + assert payload["summary"]["actionable_portfolio_issue_count"] == 2, payload + assert payload["summary"]["release_lock_ready"] is True, payload + assert payload["summary"]["evidence_consistency_ok"] is True, payload + assert payload["summary"]["public_world_class_ready"] is False, payload + assert payload["summary"]["world_class_pending_count"] == 4, payload + assert payload["opportunity_summary"]["action_type_counts"]["patch_existing_skill"] == 1, payload + assert payload["opportunity_summary"]["action_type_counts"]["agents_update"] == 1, payload + assert payload["opportunity_summary"]["action_type_counts"]["add_eval"] == 1, payload + assert all(item["write_allowed_without_approval"] is False for item in payload["curator_queue"]), payload + action_keys = {item["key"] for item in payload["actions"]} + assert {"review-ready-opportunities", "triage-skill-library", "close-world-class-evidence"} <= action_keys, payload + markdown = output_md.read_text(encoding="utf-8") + assert "Weekly SkillOps Curator Report" in markdown, markdown + assert "## Curator Queue" in markdown, markdown + assert "counts_as_world_class_evidence: `false`" in markdown, markdown + + cli_output_json = skill_dir / "reports" / "skillops" / "weekly" / "cli.json" + cli_output_md = skill_dir / "reports" / "skillops" / "weekly" / "cli.md" + cli_proc = run_command( + str(CLI), + "weekly-curator", + str(skill_dir), + "--output-json", + str(cli_output_json), + "--output-md", + str(cli_output_md), + "--generated-at", + "2026-06-16T08:00:00Z", + ) + assert cli_proc.returncode == 0, cli_proc.stderr + cli_payload = json.loads(cli_proc.stdout) + assert_contract(cli_payload) + assert cli_payload["summary"]["unique_opportunity_count"] == 3, cli_payload + assert cli_output_json.exists(), cli_output_json + assert cli_output_md.exists(), cli_output_md + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_claim_guard.py b/tests/verify_world_class_claim_guard.py new file mode 100644 index 0000000..5ac3024 --- /dev/null +++ b/tests/verify_world_class_claim_guard.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_world_class_claim_guard.py" +TMP = ROOT / "tests" / "tmp_world_class_claim_guard" +sys.path.insert(0, str(ROOT / "scripts")) +from render_world_class_claim_guard import default_claim_surfaces, rel_path # noqa: E402 + + +def run_guard(*extra: str, check: bool = True, cwd: Path = ROOT) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--generated-at", + "2026-06-14", + *extra, + ], + cwd=cwd, + capture_output=True, + text=True, + check=check, + ) + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + output_json = TMP / "world_class_claim_guard.json" + output_md = TMP / "world_class_claim_guard.md" + proc = run_guard("--output-json", str(output_json), "--output-md", str(output_md)) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + summary = payload["summary"] + assert summary["decision"] == "claim-guard-pass-evidence-pending", summary + assert summary["ledger_ready_to_claim_world_class"] is False, summary + assert summary["ledger_pending_count"] == 4, summary + assert summary["claim_surface_count"] >= 10, summary + assert summary["json_claim_surface_count"] >= 10, summary + assert summary["metadata_claim_surface_count"] >= summary["json_claim_surface_count"], summary + assert summary["package_claim_surface_count"] >= 5, summary + assert summary["violation_count"] == 0, summary + assert summary["overclaim_guard_active"] is True, summary + assert any(item["path"] == "README.md" for item in payload["scanned_surfaces"]), payload["scanned_surfaces"] + assert any(item["path"] == "manifest.json" for item in payload["scanned_surfaces"]), payload["scanned_surfaces"] + assert any(item["path"] == "evidence/world_class/README.md" for item in payload["scanned_surfaces"]), payload[ + "scanned_surfaces" + ] + assert any(item["path"] == "agents/interface.yaml" for item in payload["scanned_surfaces"]), payload[ + "scanned_surfaces" + ] + assert any(item["path"] == "security/permission_policy.json" for item in payload["scanned_surfaces"]), payload[ + "scanned_surfaces" + ] + assert not any( + item["path"].startswith("dist/install-simulation/") for item in payload["scanned_surfaces"] + ), payload["scanned_surfaces"] + assert any(item["path"] == "reports/world_class_evidence_ledger.json" for item in payload["scanned_surfaces"]), payload["scanned_surfaces"] + + surface_fixture = TMP / "surface-fixture" + for rel in [ + "README.md", + "SKILL.md", + "manifest.json", + "agents/interface.yaml", + "dist/manifest.json", + "dist/targets/openai/adapter.json", + "dist/install-simulation/simulate-skill/SKILL.md", + "security/permission_policy.json", + "evidence/world_class/README.md", + "evidence/world_class/submissions/provider-holdout.json", + ]: + target = surface_fixture / rel + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text("{}\n" if target.suffix == ".json" else "fixture\n", encoding="utf-8") + fixture_surfaces = {rel_path(path, surface_fixture) for path in default_claim_surfaces(surface_fixture)} + assert "dist/manifest.json" in fixture_surfaces, fixture_surfaces + assert "dist/targets/openai/adapter.json" in fixture_surfaces, fixture_surfaces + assert "dist/install-simulation/simulate-skill/SKILL.md" not in fixture_surfaces, fixture_surfaces + assert "evidence/world_class/submissions/provider-holdout.json" not in fixture_surfaces, fixture_surfaces + + markdown = output_md.read_text(encoding="utf-8") + assert "World-Class Claim Guard" in markdown, markdown + assert "claim-guard-pass-evidence-pending" in markdown, markdown + assert "world-class evidence ledger" in markdown, markdown + assert "JSON claim surfaces scanned" in markdown, markdown + assert "metadata claim surfaces scanned" in markdown, markdown + assert "package/runtime claim surfaces scanned" in markdown, markdown + + safe_surface = TMP / "safe.md" + safe_surface.write_text("ready to claim world-class: `false`\nworld-class evidence is pending.\n", encoding="utf-8") + safe_proc = run_guard( + "--claim-surface", str(safe_surface), + "--output-json", str(TMP / "safe_guard.json"), + "--output-md", str(TMP / "safe_guard.md"), + check=True, + ) + safe_payload = json.loads(safe_proc.stdout) + assert safe_payload["summary"]["violation_count"] == 0, safe_payload + + unsafe_surface = TMP / "unsafe.md" + unsafe_surface.write_text("ready to claim world-class: `true`\n世界级已完成\n", encoding="utf-8") + unsafe_proc = run_guard( + "--claim-surface", str(unsafe_surface), + "--output-json", str(TMP / "unsafe_guard.json"), + "--output-md", str(TMP / "unsafe_guard.md"), + check=False, + ) + assert unsafe_proc.returncode == 2, unsafe_proc.stdout + unsafe_payload = json.loads(unsafe_proc.stdout) + assert unsafe_payload["ok"] is False, unsafe_payload + assert unsafe_payload["summary"]["decision"] == "claim-blocked-overclaim", unsafe_payload + assert unsafe_payload["summary"]["violation_count"] == 2, unsafe_payload + rules = {item["rule"] for item in unsafe_payload["violations"]} + assert {"ready-to-claim-true", "zh-completion-phrase"} <= rules, unsafe_payload["violations"] + + relative_unsafe_surface = TMP / "relative-unsafe.md" + relative_unsafe_surface.write_text("world-class completed\n", encoding="utf-8") + relative_proc = run_guard( + "--claim-surface", + relative_unsafe_surface.relative_to(ROOT).as_posix(), + "--output-json", + str(TMP / "relative_unsafe_guard.json"), + "--output-md", + str(TMP / "relative_unsafe_guard.md"), + check=False, + cwd=TMP, + ) + assert relative_proc.returncode == 2, relative_proc.stdout + relative_payload = json.loads(relative_proc.stdout) + assert relative_payload["summary"]["violation_count"] == 1, relative_payload + assert relative_payload["violations"][0]["path"] == "tests/tmp_world_class_claim_guard/relative-unsafe.md", relative_payload + + unsafe_json_surface = TMP / "unsafe.json" + unsafe_json_surface.write_text('{"world_class_ready": true}\n', encoding="utf-8") + unsafe_json_proc = run_guard( + "--claim-surface", + str(unsafe_json_surface), + "--output-json", + str(TMP / "unsafe_json_guard.json"), + "--output-md", + str(TMP / "unsafe_json_guard.md"), + check=False, + ) + assert unsafe_json_proc.returncode == 2, unsafe_json_proc.stdout + unsafe_json_payload = json.loads(unsafe_json_proc.stdout) + assert unsafe_json_payload["summary"]["violation_count"] == 1, unsafe_json_payload + assert unsafe_json_payload["violations"][0]["rule"] == "json-world-class-ready-true", unsafe_json_payload + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_evidence_intake.py b/tests/verify_world_class_evidence_intake.py new file mode 100644 index 0000000..08b6bb0 --- /dev/null +++ b/tests/verify_world_class_evidence_intake.py @@ -0,0 +1,703 @@ +#!/usr/bin/env python3 +import hashlib +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_world_class_evidence_intake.py" +TMP = ROOT / "tests" / "tmp_world_class_evidence_intake" +sys.path.insert(0, str(ROOT / "scripts")) + +from world_class_evidence_contract import validate_payload # noqa: E402 +from world_class_human_fixtures import assert_human_contract_artifact_validation # noqa: E402 +from world_class_native_telemetry_fixtures import assert_native_telemetry_contract_artifact_validation # noqa: E402 + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def run_intake(*extra: str) -> dict: + extra_args = list(extra) + if "--output-json" not in extra_args: + extra_args.extend(["--output-json", str(TMP / "last_intake.json")]) + if "--output-md" not in extra_args: + extra_args.extend(["--output-md", str(TMP / "last_intake.md")]) + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--generated-at", + "2026-06-14", + *extra_args, + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + return json.loads(proc.stdout) + + +def provider_submission(*, valid: bool, artifact_path: str = "reports/output_execution_runs.json") -> dict: + return { + "schema_version": "1.0", + "evidence_key": "provider-holdout", + "template_only": False, + "category": "external", + "source_type": "provider-output-eval", + "submitted_by": "Yao provider operator", + "submitted_at": "2026-06-14", + "summary": "Aggregate provider-backed holdout evidence for ledger review.", + "artifact_refs": [ + { + "path": artifact_path, + "kind": "aggregate-report", + "contains_raw_content": not valid, + "sha256": sha256_file(ROOT / artifact_path) if valid else "example-only", + } + ], + "provenance": { + "provider": "deepseek", + "model": "deepseek-v4-flash", + "credential_material_committed": False, + }, + "privacy": { + "raw_user_content_included": False, + "raw_provider_prompt_included": False, + "credentials_included": False, + "secrets_included": False, + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "attestation": { + "real_external_or_human_evidence": valid, + "reviewer_or_operator_identity_present": valid, + "artifact_refs_reviewed": valid, + "privacy_contract_satisfied": valid, + "ledger_reviewer_approved": valid, + "ledger_reviewer": "Yao ledger reviewer" if valid else "", + "ledger_reviewed_at": "2026-06-14" if valid else "", + }, + } + + +def external_submission( + skill_root: Path, + *, + evidence_key: str, + source_type: str, + category: str = "external", + submitted_by: str = "Yao external operator", + artifact_paths: tuple[str, ...], + artifact_kinds: dict[str, str] | None = None, + provenance: dict, +) -> dict: + artifact_kinds = artifact_kinds or {} + return { + "schema_version": "1.0", + "evidence_key": evidence_key, + "template_only": False, + "category": category, + "source_type": source_type, + "submitted_by": submitted_by, + "submitted_at": "2026-06-14", + "summary": f"Completed {evidence_key} evidence for ledger review.", + "artifact_refs": [ + { + "path": path, + "kind": artifact_kinds.get(path, "aggregate-report"), + "contains_raw_content": False, + "sha256": sha256_file(skill_root / path), + } + for path in artifact_paths + ], + "provenance": provenance, + "privacy": { + "raw_user_content_included": False, + "raw_provider_prompt_included": False, + "credentials_included": False, + "secrets_included": False, + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "attestation": { + "real_external_or_human_evidence": True, + "reviewer_or_operator_identity_present": True, + "artifact_refs_reviewed": True, + "privacy_contract_satisfied": True, + "ledger_reviewer_approved": True, + "ledger_reviewer": "Yao ledger reviewer", + "ledger_reviewed_at": "2026-06-14", + }, + } + + +def write_provider_artifact(skill_root: Path, *, complete: bool) -> None: + run = { + "case_id": "provider-holdout", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model" if complete else "command", + "model_executed": complete, + "command_executed": True, + "duration_ms": 1234.5, + "provider": "openai" if complete else "local-output-eval-runner", + "model": "gpt-4.1-mini" if complete else "", + "usage": { + "input_tokens": 20, + "output_tokens": 30, + "total_tokens": 50, + "estimated": not complete, + }, + "output_sha256": "a" * 64, + "failure": "", + } + write_json( + skill_root / "reports" / "output_execution_runs.json", + { + "schema_version": "1.0", + "ok": True, + "summary": { + "variant_run_count": 2, + "model_executed_count": 2 if complete else 0, + "timing_observed_count": 2, + "token_observed_count": 2 if complete else 0, + "failure_count": 0, + }, + "runs": [run, {**run, "case_id": "provider-holdout-2"}] if complete else [run], + }, + ) + + +def provider_artifact_submission(skill_root: Path) -> dict: + return external_submission( + skill_root, + evidence_key="provider-holdout", + source_type="provider-output-eval", + artifact_paths=("reports/output_execution_runs.json",), + provenance={ + "provider": "openai", + "model": "gpt-4.1-mini", + "credential_material_committed": False, + }, + ) + + +def write_native_permission_artifacts(skill_root: Path, *, complete: bool) -> None: + capabilities = ["file_write", "network", "subprocess"] + target = { + "target": "vscode", + "status": "pass", + "adapter": "dist/targets/vscode/adapter.json", + "permission_model": "native-client", + "native_enforcement": complete, + "metadata_fallback_explicit": not complete, + "installer_enforcement": { + "target": "vscode", + "source_status": "present", + "enforced": True, + "enforced_capabilities": capabilities, + "missing_capabilities": [], + "failure_details": [], + }, + "assurance": "native-enforced" if complete else "metadata-fallback-explicit", + "declared_capabilities": capabilities, + "checks": [ + {"key": "adapter-present", "passed": True, "detail": "vscode adapter.json is readable"}, + {"key": "native-enforcement-boolean", "passed": True, "detail": "native flag is boolean"}, + ], + "failures": [], + "residual_risks": [] if complete else ["Client-native permission enforcement is not provided by this target."], + } + write_json( + skill_root / "reports" / "runtime_permission_probes.json", + { + "schema_version": "1.0", + "ok": True, + "expected_capabilities": capabilities, + "summary": { + "target_count": 1, + "pass_count": 1, + "fail_count": 0, + "native_enforcement_count": 1 if complete else 0, + "metadata_fallback_count": 0 if complete else 1, + "failure_count": 0, + "installer_enforcement_ready": True, + }, + "targets": [target], + }, + ) + write_json( + skill_root / "reports" / "install_simulation.json", + { + "schema_version": "1.0", + "ok": True, + "summary": { + "installer_permission_enforced_count": 12, + "installer_permission_failure_count": 0, + "permission_target_count": 1, + "permission_capability_count": 3, + "failure_count": 0, + }, + "checks": [ + {"id": "permission-vscode-file_write-approved", "status": "pass", "detail": "file write approved"}, + { + "id": "permission-vscode-file_write-target-enforcement", + "status": "pass", + "detail": "file write enforced", + }, + {"id": "permission-vscode-network-approved", "status": "pass", "detail": "network approved"}, + {"id": "permission-vscode-network-target-enforcement", "status": "pass", "detail": "network enforced"}, + {"id": "permission-vscode-subprocess-approved", "status": "pass", "detail": "subprocess approved"}, + { + "id": "permission-vscode-subprocess-target-enforcement", + "status": "pass", + "detail": "subprocess enforced", + }, + ], + }, + ) + + +def native_permission_submission(skill_root: Path) -> dict: + return external_submission( + skill_root, + evidence_key="native-permission-enforcement", + source_type="runtime-permission-guard", + artifact_paths=("reports/runtime_permission_probes.json", "reports/install_simulation.json"), + artifact_kinds={ + "reports/runtime_permission_probes.json": "runtime-probe-report", + "reports/install_simulation.json": "installer-enforcement-report", + }, + provenance={ + "target": "vscode", + "guard_location": "VS Code extension runtime permission guard", + "guard_scope": "target-client-native", + "guard_blocks_undeclared_capability": True, + "metadata_fallback_retained_for_other_targets": True, + }, + ) + + +def write_json(path: Path, payload: dict) -> 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 schema_contains_path_kind(schema: dict, path: str, kind: str) -> bool: + if isinstance(schema, dict): + properties = schema.get("properties", {}) + if isinstance(properties, dict): + path_schema = properties.get("path", {}) + kind_schema = properties.get("kind", {}) + if ( + isinstance(path_schema, dict) + and isinstance(kind_schema, dict) + and path_schema.get("const") == path + and kind_schema.get("const") == kind + ): + return True + return any(schema_contains_path_kind(value, path, kind) for value in schema.values()) + if isinstance(schema, list): + return any(schema_contains_path_kind(value, path, kind) for value in schema) + return False + + +def assert_external_contract_artifact_validation() -> None: + skill_root = TMP / "external_contract_root" + provider_entry = {"key": "provider-holdout", "category": "external"} + write_provider_artifact(skill_root, complete=True) + provider_valid = validate_payload( + provider_artifact_submission(skill_root), + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert provider_valid["status"] == "pass", provider_valid + provider_duplicate_ref = provider_artifact_submission(skill_root) + provider_duplicate_ref["artifact_refs"].append(dict(provider_duplicate_ref["artifact_refs"][0])) + provider_duplicate_result = validate_payload( + provider_duplicate_ref, + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert provider_duplicate_result["status"] == "fail", provider_duplicate_result + assert any("must not duplicate another artifact reference" in error for error in provider_duplicate_result["errors"]), ( + provider_duplicate_result["errors"] + ) + provider_wrong_kind = provider_artifact_submission(skill_root) + provider_wrong_kind["artifact_refs"][0]["kind"] = "supporting-evidence" + provider_wrong_kind_result = validate_payload( + provider_wrong_kind, + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert provider_wrong_kind_result["status"] == "fail", provider_wrong_kind_result + assert any("kind must be aggregate-report" in error for error in provider_wrong_kind_result["errors"]), ( + provider_wrong_kind_result["errors"] + ) + provider_wrong_filename = validate_payload( + provider_artifact_submission(skill_root), + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-copy.json", + root=skill_root, + template_expected=False, + ) + assert provider_wrong_filename["status"] == "fail", provider_wrong_filename + assert any("filename must be provider-holdout.json" in error for error in provider_wrong_filename["errors"]), provider_wrong_filename["errors"] + provider_leak = provider_artifact_submission(skill_root) + provider_leak["provenance"]["messages"] = ["raw messages must not be accepted"] + provider_leak["raw_prompt"] = "raw prompt must not be accepted" + provider_leak_result = validate_payload( + provider_leak, + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert provider_leak_result["status"] == "fail", provider_leak_result + assert any("raw content, credential, secret" in error for error in provider_leak_result["errors"]), provider_leak_result["errors"] + assert any("$.raw_prompt" in error and "$.provenance.messages" in error for error in provider_leak_result["errors"]), provider_leak_result["errors"] + provider_answer_key_leak = provider_artifact_submission(skill_root) + provider_answer_key_leak["provenance"]["Expected_Winner_Variant"] = "A" + provider_answer_key_leak["review_notes"] = [{"answer_key": "blind answer key must not be embedded"}] + provider_answer_key_leak_result = validate_payload( + provider_answer_key_leak, + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert provider_answer_key_leak_result["status"] == "fail", provider_answer_key_leak_result + assert any("answer-key fields" in error for error in provider_answer_key_leak_result["errors"]), ( + provider_answer_key_leak_result["errors"] + ) + assert any( + "$.provenance.Expected_Winner_Variant" in error and "$.review_notes[0].answer_key" in error + for error in provider_answer_key_leak_result["errors"] + ), provider_answer_key_leak_result["errors"] + write_provider_artifact(skill_root, complete=False) + provider_invalid = validate_payload( + provider_artifact_submission(skill_root), + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert provider_invalid["status"] == "fail", provider_invalid + assert any("summary.model_executed_count must be >0" in error for error in provider_invalid["errors"]), provider_invalid["errors"] + assert any("summary.token_observed_count must be >0" in error for error in provider_invalid["errors"]), provider_invalid["errors"] + + write_provider_artifact(skill_root, complete=True) + forged = json.loads((skill_root / "reports" / "output_execution_runs.json").read_text(encoding="utf-8")) + for run in forged["runs"]: + run["provider"] = "local-output-eval-runner" + write_json(skill_root / "reports" / "output_execution_runs.json", forged) + forged_provider = validate_payload( + provider_artifact_submission(skill_root), + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert forged_provider["status"] == "fail", forged_provider + assert any("matching provider, model, timing" in error for error in forged_provider["errors"]), forged_provider["errors"] + + write_provider_artifact(skill_root, complete=True) + raw_execution = json.loads((skill_root / "reports" / "output_execution_runs.json").read_text(encoding="utf-8")) + raw_execution["runs"][0]["raw_provider_prompt"] = "verbatim provider prompt must not be accepted" + raw_execution["runs"][0]["output"] = "raw model output must not be accepted" + raw_execution["run_notes"] = [{"messages": ["raw transcript-like exchange"]}] + write_json(skill_root / "reports" / "output_execution_runs.json", raw_execution) + raw_provider = validate_payload( + provider_artifact_submission(skill_root), + provider_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "provider-holdout.json", + root=skill_root, + template_expected=False, + ) + assert raw_provider["status"] == "fail", raw_provider + assert any("output_execution_runs.runs[0].raw_provider_prompt" in error for error in raw_provider["errors"]), ( + raw_provider["errors"] + ) + assert any("output_execution_runs.runs[0].output" in error for error in raw_provider["errors"]), raw_provider["errors"] + assert any("output_execution_runs.run_notes[0].messages" in error for error in raw_provider["errors"]), ( + raw_provider["errors"] + ) + + permission_entry = {"key": "native-permission-enforcement", "category": "external"} + write_native_permission_artifacts(skill_root, complete=True) + permission_valid = validate_payload( + native_permission_submission(skill_root), + permission_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-permission-enforcement.json", + root=skill_root, + template_expected=False, + ) + assert permission_valid["status"] == "pass", permission_valid + forged_probe = json.loads((skill_root / "reports" / "runtime_permission_probes.json").read_text(encoding="utf-8")) + forged_probe["targets"][0]["native_enforcement"] = False + forged_probe["targets"][0]["assurance"] = "metadata-fallback-explicit" + forged_probe["targets"][0]["metadata_fallback_explicit"] = True + forged_probe["targets"][0]["residual_risks"] = ["Fallback still requires operator enforcement."] + write_json(skill_root / "reports" / "runtime_permission_probes.json", forged_probe) + permission_forged = validate_payload( + native_permission_submission(skill_root), + permission_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-permission-enforcement.json", + root=skill_root, + template_expected=False, + ) + assert permission_forged["status"] == "fail", permission_forged + assert any("native target rows must match summary.native_enforcement_count" in error for error in permission_forged["errors"]), ( + permission_forged["errors"] + ) + write_native_permission_artifacts(skill_root, complete=True) + raw_probe = json.loads((skill_root / "reports" / "runtime_permission_probes.json").read_text(encoding="utf-8")) + raw_probe["targets"][0]["messages"] = ["raw target client transcript must not be accepted"] + write_json(skill_root / "reports" / "runtime_permission_probes.json", raw_probe) + raw_permission_probe = validate_payload( + native_permission_submission(skill_root), + permission_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-permission-enforcement.json", + root=skill_root, + template_expected=False, + ) + assert raw_permission_probe["status"] == "fail", raw_permission_probe + assert any("runtime_permission_probes.targets[0].messages" in error for error in raw_permission_probe["errors"]), ( + raw_permission_probe["errors"] + ) + + write_native_permission_artifacts(skill_root, complete=True) + raw_install = json.loads((skill_root / "reports" / "install_simulation.json").read_text(encoding="utf-8")) + raw_install["checks"][0]["raw_prompt"] = "raw installer prompt must not be accepted" + write_json(skill_root / "reports" / "install_simulation.json", raw_install) + raw_permission_install = validate_payload( + native_permission_submission(skill_root), + permission_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-permission-enforcement.json", + root=skill_root, + template_expected=False, + ) + assert raw_permission_install["status"] == "fail", raw_permission_install + assert any("install_simulation.checks[0].raw_prompt" in error for error in raw_permission_install["errors"]), ( + raw_permission_install["errors"] + ) + + write_native_permission_artifacts(skill_root, complete=False) + permission_invalid = validate_payload( + native_permission_submission(skill_root), + permission_entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-permission-enforcement.json", + root=skill_root, + template_expected=False, + ) + assert permission_invalid["status"] == "fail", permission_invalid + assert any("summary.native_enforcement_count must be >0" in error for error in permission_invalid["errors"]), permission_invalid["errors"] + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + empty_submissions = TMP / "empty-submissions" + empty_submissions.mkdir() + default_json = TMP / "world_class_evidence_intake.json" + default_md = TMP / "world_class_evidence_intake.md" + assert_human_contract_artifact_validation() + assert_external_contract_artifact_validation() + assert_native_telemetry_contract_artifact_validation() + payload = run_intake( + "--output-json", + str(default_json), + "--output-md", + str(default_md), + "--submissions-dir", + str(empty_submissions), + ) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + summary = payload["summary"] + assert summary["decision"] == "awaiting-submissions", summary + assert summary["schema_present"] is True, summary + assert summary["ledger_entry_count"] == 4, summary + assert summary["template_count"] == 4, summary + assert summary["template_pass_count"] == 4, summary + assert summary["submission_count"] == 0, summary + assert summary["valid_submission_count"] == 0, summary + assert summary["invalid_submission_count"] == 0, summary + assert summary["valid_packet_source_incomplete_count"] == 0, summary + assert summary["operator_checklist_count"] == 4, summary + assert summary["operator_checklist_ready_count"] == 0, summary + assert summary["ready_for_external_collection"] is True, summary + assert summary["ready_for_ledger_review"] is False, summary + assert summary["ready_to_claim_world_class"] is False, summary + assert summary["overclaim_guard_active"] is True, summary + assert {item["evidence_key"] for item in payload["templates"]} == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, payload["templates"] + assert all(item["status"] == "pass" and item["template_only"] is True for item in payload["templates"]), payload["templates"] + human_template = json.loads((ROOT / "evidence" / "world_class" / "templates" / "human-adjudication.intake.json").read_text(encoding="utf-8")) + decision_ref = next(item for item in human_template["artifact_refs"] if item["path"] == "reports/output_review_decisions.json") + assert "required reason" in decision_ref["note"], human_template + assert human_template["provenance"]["reviewed_at"] == "YYYY-MM-DD", human_template + assert human_template["provenance"]["reviewer_reason_required"] is True, human_template + assert "reason" in human_template["provenance"]["decision_fields"], human_template + assert "visible rubric" in human_template["privacy"]["notes"], human_template + assert "private customer data" in human_template["privacy"]["notes"], human_template + checklist = {item["evidence_key"]: item for item in payload["operator_checklist"]} + assert set(checklist) == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, checklist + assert checklist["provider-holdout"]["readiness"] == "awaiting-submission", checklist["provider-holdout"] + assert checklist["provider-holdout"]["template_path"] == "evidence/world_class/templates/provider-holdout.intake.json", checklist["provider-holdout"] + empty_submissions_rel = str(empty_submissions.relative_to(ROOT)) + assert checklist["provider-holdout"]["submission_path"] == f"{empty_submissions_rel}/provider-holdout.json", checklist["provider-holdout"] + assert checklist["provider-holdout"]["commands"]["prepare_submission"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + f"--evidence-key provider-holdout --output-dir {empty_submissions_rel}" + ), checklist["provider-holdout"] + assert checklist["provider-holdout"]["commands"]["validate_intake"] == f"python3 scripts/yao.py world-class-intake . --submissions-dir {empty_submissions_rel}", checklist["provider-holdout"] + assert checklist["provider-holdout"]["commands"]["submission_review"] == f"python3 scripts/yao.py world-class-submission-review . --submissions-dir {empty_submissions_rel}", checklist["provider-holdout"] + assert checklist["provider-holdout"]["commands"]["refresh_ledger"] == f"python3 scripts/yao.py world-class-ledger . --submissions-dir {empty_submissions_rel}", checklist["provider-holdout"] + assert "provider-backed model run" in checklist["provider-holdout"]["must_collect"]["provenance_requirements"], checklist["provider-holdout"] + assert any("--provider-runner openai" in step for step in checklist["provider-holdout"]["must_collect"]["runbook"]), checklist["provider-holdout"] + assert any("--provider-runner deepseek" in step for step in checklist["provider-holdout"]["must_collect"]["runbook"]), checklist["provider-holdout"] + assert "reports/output_execution_runs.json summary.model_executed_count > 0" in checklist["provider-holdout"]["must_collect"]["success_checks"], checklist["provider-holdout"] + assert checklist["provider-holdout"]["anti_overclaim"]["local_command_runner_counts_as_provider_model"] is False, checklist["provider-holdout"] + assert checklist["provider-holdout"]["source_accepted"] is True, checklist["provider-holdout"] + assert checklist["provider-holdout"]["observed_state"]["model_executed_count"] > 0, checklist["provider-holdout"] + assert checklist["provider-holdout"]["observed_state"]["timing_observed_count"] > 0, checklist["provider-holdout"] + assert checklist["provider-holdout"]["observed_state"]["token_observed_count"] > 0, checklist["provider-holdout"] + markdown = default_md.read_text(encoding="utf-8") + assert "World-Class Evidence Intake" in markdown, markdown + assert "ready to claim world-class: `false`" in markdown, markdown + assert "Operator Checklist" in markdown, markdown + assert "valid packet but source incomplete: `0`" in markdown, markdown + assert "operator checklist: `0` ready / `4` total" in markdown, markdown + assert "0 existing / 0 sha256 verified / 0 required verified / 1 refs" in markdown, markdown + assert f"`{empty_submissions_rel}/provider-holdout.json`" in markdown, markdown + assert f"`python3 scripts/yao.py world-class-submission-kit . --evidence-key provider-holdout --output-dir {empty_submissions_rel}`" in markdown, markdown + assert f"`python3 scripts/yao.py world-class-submission-review . --submissions-dir {empty_submissions_rel}`" in markdown, markdown + assert f"`python3 scripts/yao.py world-class-ledger . --submissions-dir {empty_submissions_rel}`" in markdown, markdown + assert "Source Runbook" in markdown, markdown + assert "--provider-runner openai" in markdown, markdown + assert "--provider-runner deepseek" in markdown, markdown + assert "" not in markdown, markdown + assert "OPENAI_API_KEY=" not in markdown, markdown + assert "Templates and planned work do not count as accepted evidence." in markdown, markdown + assert "Real submissions must include the evidence-key critical artifact paths with verified SHA-256 digests." in markdown, markdown + assert "Real submissions must replace template submitter, date, and provenance placeholders with concrete evidence metadata." in markdown, markdown + schema = json.loads((ROOT / "evidence" / "world_class" / "intake.schema.json").read_text(encoding="utf-8")) + assert schema_contains_path_kind(schema, "reports/output_execution_runs.json", "aggregate-report"), schema + assert schema_contains_path_kind(schema, "reports/output_review_decisions.json", "review-decisions"), schema + assert schema_contains_path_kind(schema, "reports/runtime_permission_probes.json", "runtime-probe-report"), schema + assert schema_contains_path_kind(schema, "reports/telemetry_hook_recipes.json", "hook-recipes"), schema + + valid_dir = TMP / "valid_submissions" + valid_dir.mkdir() + (valid_dir / "provider-holdout.json").write_text( + json.dumps(provider_submission(valid=True), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + valid_payload = run_intake("--submissions-dir", str(valid_dir)) + assert valid_payload["ok"] is True, valid_payload + assert valid_payload["summary"]["decision"] == "intake-ready-for-ledger-review", valid_payload["summary"] + assert valid_payload["summary"]["valid_submission_count"] == 1, valid_payload["summary"] + assert valid_payload["summary"]["invalid_submission_count"] == 0, valid_payload["summary"] + assert valid_payload["summary"]["valid_packet_source_incomplete_count"] == 0, valid_payload["summary"] + assert valid_payload["summary"]["operator_checklist_ready_count"] == 1, valid_payload["summary"] + assert valid_payload["summary"]["ready_for_ledger_review"] is True, valid_payload["summary"] + assert valid_payload["summary"]["ready_to_claim_world_class"] is False, valid_payload["summary"] + assert valid_payload["submissions"][0]["status"] == "pass", valid_payload["submissions"] + assert valid_payload["submissions"][0]["artifact_integrity"]["artifact_existing_count"] == 1, valid_payload["submissions"] + assert valid_payload["submissions"][0]["artifact_integrity"]["artifact_sha256_verified_count"] == 1, valid_payload["submissions"] + assert valid_payload["submissions"][0]["errors"] == [], valid_payload["submissions"] + valid_checklist = {item["evidence_key"]: item for item in valid_payload["operator_checklist"]} + assert valid_checklist["provider-holdout"]["readiness"] == "ready-for-ledger-review", valid_checklist["provider-holdout"] + assert "tests/tmp_world_class_evidence_intake/valid_submissions" in valid_checklist["provider-holdout"]["commands"]["validate_intake"], valid_checklist["provider-holdout"] + assert "tests/tmp_world_class_evidence_intake/valid_submissions" in valid_checklist["provider-holdout"]["commands"]["submission_review"], valid_checklist["provider-holdout"] + assert "tests/tmp_world_class_evidence_intake/valid_submissions" in valid_checklist["provider-holdout"]["commands"]["refresh_ledger"], valid_checklist["provider-holdout"] + + placeholder_dir = TMP / "placeholder_submissions" + placeholder_dir.mkdir() + placeholder_submission = provider_submission(valid=True) + placeholder_submission["submitted_by"] = "operator with provider credentials" + placeholder_submission["submitted_at"] = "YYYY-MM-DD" + (placeholder_dir / "provider-holdout.json").write_text( + json.dumps(placeholder_submission, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + placeholder_payload = run_intake("--submissions-dir", str(placeholder_dir)) + assert placeholder_payload["ok"] is False, placeholder_payload + assert placeholder_payload["summary"]["decision"] == "fix-intake", placeholder_payload["summary"] + assert placeholder_payload["summary"]["invalid_submission_count"] == 1, placeholder_payload["summary"] + placeholder_errors = placeholder_payload["submissions"][0]["errors"] + assert any("submitted_by must not use template placeholder text" in error for error in placeholder_errors), placeholder_errors + assert any("submitted_at must use YYYY-MM-DD" in error for error in placeholder_errors), placeholder_errors + + invalid_dir = TMP / "invalid_submissions" + invalid_dir.mkdir() + (invalid_dir / "provider-holdout.json").write_text( + json.dumps(provider_submission(valid=False), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + invalid_payload = run_intake("--submissions-dir", str(invalid_dir)) + assert invalid_payload["ok"] is False, invalid_payload + assert invalid_payload["summary"]["decision"] == "fix-intake", invalid_payload["summary"] + assert invalid_payload["summary"]["invalid_submission_count"] == 1, invalid_payload["summary"] + assert invalid_payload["summary"]["operator_checklist_ready_count"] == 0, invalid_payload["summary"] + assert any("raw content" in error for error in invalid_payload["submissions"][0]["errors"]), invalid_payload["submissions"] + assert any("sha256" in error for error in invalid_payload["submissions"][0]["errors"]), invalid_payload["submissions"] + assert any("attestation.real_external_or_human_evidence" in error for error in invalid_payload["submissions"][0]["errors"]), invalid_payload["submissions"] + invalid_checklist = {item["evidence_key"]: item for item in invalid_payload["operator_checklist"]} + assert invalid_checklist["provider-holdout"]["readiness"] == "fix-submission", invalid_checklist["provider-holdout"] + + unrelated_dir = TMP / "unrelated_artifact_submissions" + unrelated_dir.mkdir() + (unrelated_dir / "provider-holdout.json").write_text( + json.dumps(provider_submission(valid=True, artifact_path="reports/context_budget.json"), ensure_ascii=False, indent=2) + + "\n", + encoding="utf-8", + ) + unrelated_payload = run_intake("--submissions-dir", str(unrelated_dir)) + assert unrelated_payload["ok"] is False, unrelated_payload + assert unrelated_payload["summary"]["decision"] == "fix-intake", unrelated_payload["summary"] + assert unrelated_payload["summary"]["invalid_submission_count"] == 1, unrelated_payload["summary"] + unrelated_errors = unrelated_payload["submissions"][0]["errors"] + assert any("required evidence artifact reports/output_execution_runs.json" in error for error in unrelated_errors), unrelated_errors + assert unrelated_payload["submissions"][0]["artifact_integrity"]["artifact_sha256_verified_count"] == 1, unrelated_payload["submissions"] + assert unrelated_payload["submissions"][0]["artifact_integrity"]["required_artifact_verified_count"] == 0, unrelated_payload["submissions"] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_evidence_ledger.py b/tests/verify_world_class_evidence_ledger.py new file mode 100644 index 0000000..430a1b1 --- /dev/null +++ b/tests/verify_world_class_evidence_ledger.py @@ -0,0 +1,587 @@ +#!/usr/bin/env python3 +import hashlib +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_world_class_evidence_ledger.py" +TMP = ROOT / "tests" / "tmp_world_class_evidence_ledger" + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def provider_submission(artifact_root: Path = ROOT, artifact_path: str = "reports/output_execution_runs.json") -> dict: + return { + "schema_version": "1.0", + "evidence_key": "provider-holdout", + "template_only": False, + "category": "external", + "source_type": "provider-output-eval", + "submitted_by": "Yao provider operator", + "submitted_at": "2026-06-13", + "summary": "Aggregate provider-backed holdout evidence for ledger review.", + "artifact_refs": [ + { + "path": artifact_path, + "kind": "aggregate-report", + "contains_raw_content": False, + "sha256": sha256_file(artifact_root / artifact_path), + } + ], + "provenance": { + "provider": "deepseek", + "model": "deepseek-v4-flash", + "credential_material_committed": False, + }, + "privacy": { + "raw_user_content_included": False, + "raw_provider_prompt_included": False, + "credentials_included": False, + "secrets_included": False, + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "attestation": { + "real_external_or_human_evidence": True, + "reviewer_or_operator_identity_present": True, + "artifact_refs_reviewed": True, + "privacy_contract_satisfied": True, + "ledger_reviewer_approved": True, + "ledger_reviewer": "Yao ledger reviewer", + "ledger_reviewed_at": "2026-06-13", + }, + } + + +def accepted_provider_execution_report() -> dict: + return { + "schema_version": "1.0", + "ok": True, + "summary": { + "variant_run_count": 1, + "model_executed_count": 1, + "timing_observed_count": 1, + "token_observed_count": 1, + "failure_count": 0, + }, + "runs": [ + { + "case_id": "provider-holdout", + "variant": "with_skill", + "status": "pass", + "execution_mode": "model", + "model_executed": True, + "command_executed": True, + "duration_ms": 1234.5, + "provider": "deepseek", + "model": "deepseek-v4-flash", + "usage": { + "input_tokens": 20, + "output_tokens": 30, + "total_tokens": 50, + "estimated": False, + }, + "output_sha256": "a" * 64, + "failure": "", + } + ], + } + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + empty_submissions = TMP / "empty-submissions" + empty_submissions.mkdir() + output_json = TMP / "world_class_evidence_ledger.json" + output_md = TMP / "world_class_evidence_ledger.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--submissions-dir", + str(empty_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + summary = payload["summary"] + assert payload["report_contract"]["contract"] == "world-class-evidence-ledger", payload + assert payload["report_contract"]["top_level_mirrors_summary"] is True, payload + for field in payload["report_contract"]["summary_fields"]: + assert payload[field] == summary[field], (field, payload[field], summary[field]) + assert summary["ledger_entry_count"] == 4, summary + assert summary["accepted_count"] == 0, summary + assert summary["pending_count"] == 4, summary + assert summary["human_pending_count"] == 1, summary + assert summary["external_pending_count"] == 3, summary + assert summary["submitted_entry_count"] == 0, summary + assert summary["missing_submission_count"] == 4, summary + assert summary["invalid_submission_count"] == 0, summary + assert summary["source_check_count"] >= 13, summary + assert summary["source_pass_count"] + summary["source_blocked_count"] == summary["source_check_count"], summary + assert summary["source_blocked_count"] >= 6, summary + assert summary["submitted_but_pending_count"] == 0, summary + assert summary["overclaim_guard_active"] is True, summary + assert summary["ready_to_claim_world_class"] is False, summary + assert payload["ready_to_claim_world_class"] is False, payload + assert payload["pending_count"] == 4, payload + assert payload["accepted_count"] == 0, payload + assert payload["source_blocked_count"] == summary["source_blocked_count"], payload + assert payload["artifacts"]["intake"] == "reports/world_class_evidence_intake.md", payload + entries = {entry["key"]: entry for entry in payload["entries"]} + assert set(entries) == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, entries + assert entries["provider-holdout"]["source_accepted"] is True, entries["provider-holdout"] + assert entries["provider-holdout"]["observed_state"]["model_executed_count"] > 0, entries["provider-holdout"] + assert any("--provider-runner openai" in step for step in entries["provider-holdout"]["runbook"]), entries["provider-holdout"] + assert any("--provider-runner deepseek" in step for step in entries["provider-holdout"]["runbook"]), entries["provider-holdout"] + assert not any("" in step or "OPENAI_API_KEY=" in step for step in entries["provider-holdout"]["runbook"]), entries["provider-holdout"] + provider_source = {item["field"]: item for item in entries["provider-holdout"]["source_checklist"]} + assert provider_source["model_executed_count"]["status"] == "pass", provider_source + assert provider_source["timing_observed_count"]["status"] == "pass", provider_source + assert provider_source["token_observed_count"]["status"] == "pass", provider_source + assert entries["provider-holdout"]["submission_state"]["status"] == "missing", entries["provider-holdout"] + assert entries["provider-holdout"]["submission_state"]["ledger_counts_as_completion"] is False, entries["provider-holdout"] + assert entries["human-adjudication"]["observed_state"]["pending_count"] == 5, entries["human-adjudication"] + assert entries["human-adjudication"]["observed_state"]["reviewer_metadata_present"] is False, entries["human-adjudication"] + assert entries["human-adjudication"]["observed_state"]["reason_required"] is True, entries["human-adjudication"] + assert entries["human-adjudication"]["observed_state"]["raw_content_allowed"] is False, entries["human-adjudication"] + assert entries["human-adjudication"]["observed_state"]["raw_content_path_count"] == 0, entries["human-adjudication"] + assert entries["human-adjudication"]["observed_state"]["ready_for_human_evidence"] is False, entries["human-adjudication"] + human_source = {item["field"]: item for item in entries["human-adjudication"]["source_checklist"]} + assert human_source["reviewer_metadata_present"]["status"] == "blocked", human_source + assert human_source["reason_required"]["status"] == "pass", human_source + assert human_source["raw_content_allowed"]["status"] == "pass", human_source + assert human_source["ready_for_human_evidence"]["status"] == "blocked", human_source + assert "choice and reason" in human_source["pending_count"]["next_action"], human_source + assert "metadata and rationale" in human_source["ready_for_human_evidence"]["next_action"], human_source + assert entries["native-permission-enforcement"]["observed_state"]["native_enforcement_count"] == 0, entries["native-permission-enforcement"] + assert entries["native-permission-enforcement"]["observed_state"]["installer_enforcement_pass_count"] >= 0, entries["native-permission-enforcement"] + assert any("summary.failure_count == 0" in check for check in entries["native-permission-enforcement"]["success_checks"]), entries["native-permission-enforcement"] + assert entries["native-client-telemetry"]["observed_state"]["external_source_events"] == 0, entries["native-client-telemetry"] + for entry in entries.values(): + assert entry["status"] == "pending", entry + assert entry["success_checks"], entry + assert entry["privacy_contract"], entry + assert entry["anti_overclaim"]["planned_work_counts_as_evidence"] is False, entry + assert entry["anti_overclaim"]["pending_review_counts_as_human_decision"] is False, entry + assert "submission_state" in entry, entry + markdown = output_md.read_text(encoding="utf-8") + assert "World-Class Evidence Ledger" in markdown, markdown + assert "overclaim guard active: `true`" in markdown, markdown + assert "submitted entries: `0`" in markdown, markdown + assert "source checks:" in markdown, markdown + assert "Source Runbook" in markdown, markdown + assert "--provider-runner openai" in markdown, markdown + assert "--provider-runner deepseek" in markdown, markdown + assert "" not in markdown, markdown + assert "OPENAI_API_KEY=" not in markdown, markdown + assert "Source Evidence Checks" in markdown, markdown + assert "| Provider model run | `10` | `>0` | `pass` |" in markdown, markdown + assert "`provider-holdout`" in markdown, markdown + + submissions = TMP / "submissions" + submissions.mkdir() + (submissions / "provider-holdout.json").write_text( + json.dumps(provider_submission(), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + submitted_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(TMP / "submitted_ledger.json"), + "--output-md", + str(TMP / "submitted_ledger.md"), + "--submissions-dir", + str(submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + submitted_payload = json.loads(submitted_proc.stdout) + submitted_summary = submitted_payload["summary"] + assert submitted_summary["submitted_entry_count"] == 1, submitted_summary + assert submitted_summary["submitted_but_pending_count"] == 0, submitted_summary + assert submitted_summary["invalid_submission_count"] == 0, submitted_summary + assert submitted_summary["accepted_count"] == 1, submitted_summary + assert submitted_summary["pending_count"] == 3, submitted_summary + assert submitted_summary["source_blocked_count"] >= 6, submitted_summary + submitted_provider = {entry["key"]: entry for entry in submitted_payload["entries"]}["provider-holdout"] + assert submitted_provider["status"] == "accepted", submitted_provider + assert submitted_provider["submission_state"]["status"] == "submitted", submitted_provider + assert submitted_provider["submission_state"]["artifact_sha256_verified_count"] == 1, submitted_provider + assert submitted_provider["submission_state"]["attested_real_evidence"] is True, submitted_provider + assert submitted_provider["submission_state"]["ledger_counts_as_completion"] is False, submitted_provider + assert submitted_provider["submission_state"]["errors"] == [], submitted_provider + + accepted_source_skill = TMP / "accepted_source_skill" + (accepted_source_skill / "reports").mkdir(parents=True) + (accepted_source_skill / "reports" / "output_execution_runs.json").write_text( + json.dumps(accepted_provider_execution_report(), ensure_ascii=False, indent=2) + + "\n", + encoding="utf-8", + ) + accepted_source_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "accepted_source_without_submission.json"), + "--output-md", + str(TMP / "accepted_source_without_submission.md"), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + accepted_source_payload = json.loads(accepted_source_proc.stdout) + accepted_source_summary = accepted_source_payload["summary"] + assert accepted_source_summary["source_accepted_count"] == 1, accepted_source_summary + assert accepted_source_summary["accepted_count"] == 0, accepted_source_summary + assert accepted_source_summary["source_accepted_without_valid_submission_count"] == 1, accepted_source_summary + assert accepted_source_summary["source_check_count"] >= 13, accepted_source_summary + accepted_source_provider = { + entry["key"]: entry for entry in accepted_source_payload["entries"] + }["provider-holdout"] + assert accepted_source_provider["source_accepted"] is True, accepted_source_provider + assert accepted_source_provider["status"] == "pending", accepted_source_provider + assert accepted_source_provider["submission_state"]["status"] == "missing", accepted_source_provider + assert all(item["status"] == "pass" for item in accepted_source_provider["source_checklist"]), accepted_source_provider + + (accepted_source_skill / "reports" / "context_budget.json").write_text( + json.dumps({"summary": {"unrelated": True}}, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + unrelated_accepted_submissions = TMP / "unrelated_accepted_submissions" + unrelated_accepted_submissions.mkdir() + (unrelated_accepted_submissions / "provider-holdout.json").write_text( + json.dumps( + provider_submission(accepted_source_skill, artifact_path="reports/context_budget.json"), + ensure_ascii=False, + indent=2, + ) + + "\n", + encoding="utf-8", + ) + unrelated_accepted_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "unrelated_accepted_provider_ledger.json"), + "--output-md", + str(TMP / "unrelated_accepted_provider_ledger.md"), + "--submissions-dir", + str(unrelated_accepted_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + unrelated_accepted_payload = json.loads(unrelated_accepted_proc.stdout) + unrelated_accepted_summary = unrelated_accepted_payload["summary"] + assert unrelated_accepted_summary["source_accepted_count"] == 1, unrelated_accepted_summary + assert unrelated_accepted_summary["accepted_count"] == 0, unrelated_accepted_summary + assert unrelated_accepted_summary["invalid_submission_count"] == 1, unrelated_accepted_summary + unrelated_accepted_provider = { + entry["key"]: entry for entry in unrelated_accepted_payload["entries"] + }["provider-holdout"] + assert unrelated_accepted_provider["source_accepted"] is True, unrelated_accepted_provider + assert unrelated_accepted_provider["status"] == "pending", unrelated_accepted_provider + assert unrelated_accepted_provider["submission_state"]["status"] == "invalid-contract", unrelated_accepted_provider + assert any( + "required evidence artifact reports/output_execution_runs.json" in error + for error in unrelated_accepted_provider["submission_state"]["errors"] + ), unrelated_accepted_provider + + placeholder_accepted_submissions = TMP / "placeholder_accepted_submissions" + placeholder_accepted_submissions.mkdir() + placeholder_submission = provider_submission(accepted_source_skill) + placeholder_submission["submitted_by"] = "operator with provider credentials" + placeholder_submission["submitted_at"] = "YYYY-MM-DD" + (placeholder_accepted_submissions / "provider-holdout.json").write_text( + json.dumps(placeholder_submission, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + placeholder_accepted_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "placeholder_accepted_provider_ledger.json"), + "--output-md", + str(TMP / "placeholder_accepted_provider_ledger.md"), + "--submissions-dir", + str(placeholder_accepted_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + placeholder_accepted_payload = json.loads(placeholder_accepted_proc.stdout) + placeholder_accepted_summary = placeholder_accepted_payload["summary"] + assert placeholder_accepted_summary["source_accepted_count"] == 1, placeholder_accepted_summary + assert placeholder_accepted_summary["accepted_count"] == 0, placeholder_accepted_summary + assert placeholder_accepted_summary["invalid_submission_count"] == 1, placeholder_accepted_summary + placeholder_accepted_provider = { + entry["key"]: entry for entry in placeholder_accepted_payload["entries"] + }["provider-holdout"] + assert placeholder_accepted_provider["source_accepted"] is True, placeholder_accepted_provider + assert placeholder_accepted_provider["status"] == "pending", placeholder_accepted_provider + assert placeholder_accepted_provider["submission_state"]["status"] == "invalid-contract", placeholder_accepted_provider + assert any( + "submitted_by must not use template placeholder text" in error + for error in placeholder_accepted_provider["submission_state"]["errors"] + ), placeholder_accepted_provider + + unapproved_submissions = TMP / "unapproved_submissions" + unapproved_submissions.mkdir() + unapproved_submission = provider_submission(accepted_source_skill) + unapproved_submission["attestation"]["ledger_reviewer_approved"] = False + (unapproved_submissions / "provider-holdout.json").write_text( + json.dumps(unapproved_submission, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + unapproved_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "unapproved_provider_ledger.json"), + "--output-md", + str(TMP / "unapproved_provider_ledger.md"), + "--submissions-dir", + str(unapproved_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + unapproved_payload = json.loads(unapproved_proc.stdout) + unapproved_summary = unapproved_payload["summary"] + assert unapproved_summary["source_accepted_count"] == 1, unapproved_summary + assert unapproved_summary["submitted_entry_count"] == 0, unapproved_summary + assert unapproved_summary["reviewer_approved_submission_count"] == 0, unapproved_summary + assert unapproved_summary["accepted_count"] == 0, unapproved_summary + unapproved_provider = { + entry["key"]: entry for entry in unapproved_payload["entries"] + }["provider-holdout"] + assert unapproved_provider["source_accepted"] is True, unapproved_provider + assert unapproved_provider["status"] == "pending", unapproved_provider + assert unapproved_provider["submission_state"]["status"] == "invalid-contract", unapproved_provider + assert unapproved_provider["submission_state"]["ledger_reviewer_approved"] is False, unapproved_provider + assert any( + "attestation.ledger_reviewer_approved must be true" in error + for error in unapproved_provider["submission_state"]["errors"] + ), unapproved_provider + + missing_reviewer_submissions = TMP / "missing_reviewer_submissions" + missing_reviewer_submissions.mkdir() + missing_reviewer_submission = provider_submission(accepted_source_skill) + missing_reviewer_submission["attestation"]["ledger_reviewer"] = "" + (missing_reviewer_submissions / "provider-holdout.json").write_text( + json.dumps(missing_reviewer_submission, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + missing_reviewer_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "missing_reviewer_provider_ledger.json"), + "--output-md", + str(TMP / "missing_reviewer_provider_ledger.md"), + "--submissions-dir", + str(missing_reviewer_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + missing_reviewer_payload = json.loads(missing_reviewer_proc.stdout) + missing_reviewer_provider = { + entry["key"]: entry for entry in missing_reviewer_payload["entries"] + }["provider-holdout"] + assert missing_reviewer_provider["status"] == "pending", missing_reviewer_provider + assert missing_reviewer_provider["submission_state"]["status"] == "invalid-contract", missing_reviewer_provider + assert any( + "attestation.ledger_reviewer is required" in error + for error in missing_reviewer_provider["submission_state"]["errors"] + ), missing_reviewer_provider + + self_approved_submissions = TMP / "self_approved_submissions" + self_approved_submissions.mkdir() + self_approved_submission = provider_submission(accepted_source_skill) + self_approved_submission["attestation"]["ledger_reviewer"] = self_approved_submission["submitted_by"] + (self_approved_submissions / "provider-holdout.json").write_text( + json.dumps(self_approved_submission, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + self_approved_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "self_approved_provider_ledger.json"), + "--output-md", + str(TMP / "self_approved_provider_ledger.md"), + "--submissions-dir", + str(self_approved_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + self_approved_payload = json.loads(self_approved_proc.stdout) + self_approved_provider = { + entry["key"]: entry for entry in self_approved_payload["entries"] + }["provider-holdout"] + assert self_approved_provider["status"] == "pending", self_approved_provider + assert self_approved_provider["submission_state"]["status"] == "invalid-contract", self_approved_provider + assert any( + "attestation.ledger_reviewer must be different from submitted_by" in error + for error in self_approved_provider["submission_state"]["errors"] + ), self_approved_provider + + early_review_submissions = TMP / "early_review_submissions" + early_review_submissions.mkdir() + early_review_submission = provider_submission(accepted_source_skill) + early_review_submission["submitted_at"] = "2026-06-13" + early_review_submission["attestation"]["ledger_reviewed_at"] = "2026-06-12" + (early_review_submissions / "provider-holdout.json").write_text( + json.dumps(early_review_submission, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + early_review_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "early_review_provider_ledger.json"), + "--output-md", + str(TMP / "early_review_provider_ledger.md"), + "--submissions-dir", + str(early_review_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + early_review_payload = json.loads(early_review_proc.stdout) + early_review_provider = { + entry["key"]: entry for entry in early_review_payload["entries"] + }["provider-holdout"] + assert early_review_provider["source_accepted"] is True, early_review_provider + assert early_review_provider["status"] == "pending", early_review_provider + assert early_review_provider["submission_state"]["status"] == "invalid-contract", early_review_provider + assert any( + "attestation.ledger_reviewed_at must be at or after submitted_at" in error + for error in early_review_provider["submission_state"]["errors"] + ), early_review_provider + + accepted_submissions = TMP / "accepted_submissions" + accepted_submissions.mkdir() + (accepted_submissions / "provider-holdout.json").write_text( + json.dumps(provider_submission(accepted_source_skill), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + accepted_proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(accepted_source_skill), + "--output-json", + str(TMP / "accepted_provider_ledger.json"), + "--output-md", + str(TMP / "accepted_provider_ledger.md"), + "--submissions-dir", + str(accepted_submissions), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + accepted_payload = json.loads(accepted_proc.stdout) + accepted_summary = accepted_payload["summary"] + assert accepted_summary["source_accepted_count"] == 1, accepted_summary + assert accepted_summary["accepted_count"] == 1, accepted_summary + assert accepted_summary["reviewer_approved_submission_count"] == 1, accepted_summary + assert accepted_summary["pending_count"] == 3, accepted_summary + assert accepted_summary["source_accepted_without_valid_submission_count"] == 0, accepted_summary + accepted_provider = {entry["key"]: entry for entry in accepted_payload["entries"]}["provider-holdout"] + assert accepted_provider["status"] == "accepted", accepted_provider + assert accepted_provider["submission_state"]["status"] == "submitted", accepted_provider + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_evidence_plan.py b/tests/verify_world_class_evidence_plan.py new file mode 100644 index 0000000..96b9cd4 --- /dev/null +++ b/tests/verify_world_class_evidence_plan.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_world_class_evidence_plan.py" +TMP = ROOT / "tests" / "tmp_world_class_evidence" + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + output_json = TMP / "world_class_evidence_plan.json" + output_md = TMP / "world_class_evidence_plan.md" + proc = subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--generated-at", + "2026-06-13", + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + assert payload["summary"]["decision"] == "collect-external-evidence", payload + assert payload["summary"]["ready_to_claim_world_class"] is False, payload + assert payload["summary"]["ledger_completion_required"] is True, payload + assert payload["summary"]["evidence_requirement_count"] == 4, payload + assert payload["summary"]["task_count"] == 4, payload + assert payload["summary"]["human_task_count"] == 1, payload + assert payload["summary"]["external_task_count"] == 3, payload + assert payload["artifacts"]["ledger"] == "reports/world_class_evidence_ledger.md", payload + assert payload["artifacts"]["intake"] == "reports/world_class_evidence_intake.md", payload + tasks = {item["key"]: item for item in payload["tasks"]} + requirements = {item["key"]: item for item in payload["evidence_requirements"]} + assert set(tasks) == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, tasks + assert set(requirements) == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, requirements + assert requirements["provider-holdout"]["status"] == "pass", requirements["provider-holdout"] + provider_runbook = requirements["provider-holdout"]["runbook"] + assert any("--provider-runner openai" in command for command in provider_runbook), requirements["provider-holdout"] + assert any("--provider-runner deepseek" in command for command in provider_runbook), requirements["provider-holdout"] + assert any(command.startswith("Set one provider API key") for command in provider_runbook), requirements["provider-holdout"] + assert any("DEEPSEEK_API_KEY" in command for command in provider_runbook), requirements["provider-holdout"] + assert not any("" in command or "OPENAI_API_KEY=" in command for command in provider_runbook), requirements["provider-holdout"] + assert any("world-class-intake . --submissions-dir evidence/world_class/submissions" in command for command in provider_runbook), requirements["provider-holdout"] + assert any("evidence/world_class/templates/provider-holdout.intake.json" in command for command in provider_runbook), requirements["provider-holdout"] + assert tasks["provider-holdout"]["status"] == "pass", tasks["provider-holdout"] + assert any("--provider-runner deepseek" in command for command in tasks["provider-holdout"]["runbook"]), tasks["provider-holdout"] + assert "reports/output_review_decisions.json" in tasks["human-adjudication"]["evidence_artifacts"], tasks["human-adjudication"] + assert any("output-review-import" in command for command in tasks["human-adjudication"]["runbook"]), tasks["human-adjudication"] + assert any("required reason" in command for command in tasks["human-adjudication"]["runbook"]), tasks["human-adjudication"] + assert any("summary.reviewer_metadata_present is true" in check for check in tasks["human-adjudication"]["success_checks"]), tasks["human-adjudication"] + assert any("summary.ready_for_human_evidence is true" in check for check in tasks["human-adjudication"]["success_checks"]), tasks["human-adjudication"] + assert "scripts/import_output_review_decisions.py" in tasks["human-adjudication"]["evidence_artifacts"], tasks["human-adjudication"] + assert any("importer rejects raw prompt" in item for item in tasks["human-adjudication"]["privacy_contract"]), tasks["human-adjudication"] + assert any("Reviewer reasons must be rubric-based" in item for item in tasks["human-adjudication"]["privacy_contract"]), tasks["human-adjudication"] + assert any("runtime-permissions" in command for command in tasks["native-permission-enforcement"]["runbook"]), tasks["native-permission-enforcement"] + assert any("install-simulate" in command for command in tasks["native-permission-enforcement"]["runbook"]), tasks["native-permission-enforcement"] + assert any("summary.failure_count == 0" in check for check in tasks["native-permission-enforcement"]["success_checks"]), tasks["native-permission-enforcement"] + assert any("installer_enforcement_pass_count" in check for check in tasks["native-permission-enforcement"]["success_checks"]), tasks["native-permission-enforcement"] + assert any("telemetry_native_host.py" in command for command in tasks["native-client-telemetry"]["runbook"]), tasks["native-client-telemetry"] + for task in tasks.values(): + assert task["success_checks"], task + assert task["evidence_artifacts"], task + assert "reports/world_class_evidence_intake.json" in task["evidence_artifacts"], task + assert task["privacy_contract"], task + markdown = output_md.read_text(encoding="utf-8") + assert "World-Class Evidence Plan" in markdown, markdown + assert "tasks: `4`" in markdown, markdown + assert "ready to claim world-class: `false`" in markdown, markdown + assert "ledger completion required: `true`" in markdown, markdown + assert "" not in markdown, markdown + assert "OPENAI_API_KEY=" not in markdown, markdown + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_operator_runbook.py b/tests/verify_world_class_operator_runbook.py new file mode 100644 index 0000000..fef0414 --- /dev/null +++ b/tests/verify_world_class_operator_runbook.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python3 +import hashlib +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_world_class_operator_runbook.py" +CLI = ROOT / "scripts" / "yao.py" +TMP = ROOT / "tests" / "tmp_world_class_operator_runbook" + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def provider_submission() -> dict: + return { + "schema_version": "1.0", + "evidence_key": "provider-holdout", + "template_only": False, + "category": "external", + "source_type": "provider-output-eval", + "submitted_by": "Yao provider operator", + "submitted_at": "2026-06-14", + "summary": "Aggregate provider-backed holdout evidence for ledger review.", + "artifact_refs": [ + { + "path": "reports/output_execution_runs.json", + "kind": "aggregate-report", + "contains_raw_content": False, + "sha256": sha256_file(ROOT / "reports" / "output_execution_runs.json"), + } + ], + "provenance": {"provider": "deepseek", "model": "deepseek-v4-flash", "credential_material_committed": False}, + "privacy": { + "raw_user_content_included": False, + "raw_provider_prompt_included": False, + "credentials_included": False, + "secrets_included": False, + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "attestation": { + "real_external_or_human_evidence": True, + "reviewer_or_operator_identity_present": True, + "artifact_refs_reviewed": True, + "privacy_contract_satisfied": True, + "ledger_reviewer_approved": True, + "ledger_reviewer": "Yao ledger reviewer", + "ledger_reviewed_at": "2026-06-14", + }, + } + + +def run_direct(*extra: str) -> dict: + proc = subprocess.run( + [sys.executable, str(SCRIPT), str(ROOT), "--generated-at", "2026-06-14", *extra], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + return json.loads(proc.stdout) + + +def run_cli(*extra: str) -> dict: + env = dict(os.environ) + env["YAO_CLI_TELEMETRY"] = "0" + env.pop("YAO_CLI_TELEMETRY_EVENTS", None) + proc = subprocess.run( + [sys.executable, str(CLI), *extra], + cwd=ROOT, + capture_output=True, + text=True, + env=env, + check=True, + ) + return json.loads(proc.stdout) + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + empty_submissions = TMP / "empty-submissions" + empty_submissions.mkdir() + output_json = TMP / "world_class_operator_runbook.json" + output_md = TMP / "world_class_operator_runbook.md" + output_html = TMP / "world_class_operator_runbook.html" + payload = run_direct( + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--output-html", + str(output_html), + "--submissions-dir", + str(empty_submissions), + ) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + summary = payload["summary"] + assert summary["decision"] == "collect-evidence", summary + assert summary["evidence_item_count"] == 4, summary + assert summary["pending_count"] == 4, summary + assert summary["awaiting_submission_count"] == 4, summary + assert summary["ready_for_ledger_review_count"] == 0, summary + assert summary["valid_packet_source_incomplete_count"] == 0, summary + assert summary["invalid_submission_count"] == 0, summary + assert summary["source_check_count"] >= 13, summary + assert summary["source_pass_count"] + summary["source_blocked_count"] == summary["source_check_count"], summary + assert summary["source_blocked_count"] >= 6, summary + assert summary["repair_checklist_count"] >= summary["source_blocked_count"], summary + assert summary["phase_queue_count"] == 2, summary + assert summary["phase_queue_blocked_count"] == 2, summary + assert summary["phase_queue_row_count"] == summary["repair_checklist_count"], summary + assert summary["phase_queue_next_phase"] == "unblock-access", summary + assert summary["phase_queue_counts_as_completion"] is False, summary + assert summary["coordination_step_count"] == 6, summary + assert summary["coordination_user_required_step_count"] == 6, summary + assert summary["coordination_pending_evidence_keys"] == [ + "human-adjudication", + "native-client-telemetry", + "native-permission-enforcement", + "provider-holdout", + ], summary + assert summary["coordination_counts_as_completion"] is False, summary + assert summary["release_gate_ready"] is False, summary + assert summary["release_gate_blocked_count"] == 4, summary + assert summary["release_gate_check_count"] == 5, summary + assert summary["release_gate_counts_as_completion"] is False, summary + assert summary["ready_to_claim_world_class"] is False, summary + assert summary["runbook_counts_as_completion"] is False, summary + assert payload["repair_checklist"], payload + assert len(payload["phase_queue"]) == summary["phase_queue_count"], payload["phase_queue"] + assert sum(item["row_count"] for item in payload["phase_queue"]) == summary["phase_queue_row_count"], payload[ + "phase_queue" + ] + items = {item["evidence_key"]: item for item in payload["items"]} + assert set(items) == { + "provider-holdout", + "human-adjudication", + "native-permission-enforcement", + "native-client-telemetry", + }, items + coordination_plan = payload["coordination_plan"] + assert len(coordination_plan) == summary["coordination_step_count"], coordination_plan + assert all(step["counts_as_completion"] is False for step in coordination_plan), coordination_plan + coordination_by_key = {step["evidence_key"]: step for step in coordination_plan if step["evidence_key"]} + assert set(coordination_by_key) == set(items), coordination_by_key + assert "output-exec . --provider-runner " in coordination_by_key["provider-holdout"]["command"], ( + coordination_by_key + ) + assert "output-review-kit" in coordination_by_key["human-adjudication"]["command"], coordination_by_key + assert "runtime-permissions" in coordination_by_key["native-permission-enforcement"]["command"], ( + coordination_by_key + ) + assert "telemetry-import" in coordination_by_key["native-client-telemetry"]["command"], coordination_by_key + assert payload["release_gate"]["ready"] is False, payload["release_gate"] + assert payload["release_gate"]["blocked_count"] == summary["release_gate_blocked_count"], payload["release_gate"] + assert payload["release_gate"]["check_count"] == summary["release_gate_check_count"], payload["release_gate"] + assert payload["release_gate"]["counts_as_completion"] is False, payload["release_gate"] + release_checks = {item["key"]: item for item in payload["release_gate"]["checks"]} + assert release_checks["world_class_ledger_ready"]["passed"] is False, release_checks + assert release_checks["claim_guard_clean"]["passed"] is False, release_checks + assert release_checks["benchmark_public_claim_ready"]["passed"] is False, release_checks + assert release_checks["review_studio_clean"]["passed"] is False, release_checks + assert release_checks["evidence_consistency_clean"]["passed"] is True, release_checks + provider = items["provider-holdout"] + assert provider["review_state"] == "awaiting-submission", provider + assert provider["source_accepted"] is True, provider + assert provider["blocked_source_check_count"] == 0, provider + assert provider["repair_blocked_count"] == 1, provider + assert provider["repair_counts_as_completion"] is False, provider + assert provider["phase_queue_blocked_count"] == 1, provider + assert provider["phase_queue_counts_as_completion"] is False, provider + assert [item["phase"] for item in provider["phase_queue"]] == ["unblock-access"], provider + assert any("--provider-runner openai" in step for step in provider["execution_runbook"]), provider + assert any("--provider-runner deepseek" in step for step in provider["execution_runbook"]), provider + assert not any("" in step or "OPENAI_API_KEY=" in step for step in provider["execution_runbook"]), provider + assert provider["next_source_actions"] == [], provider + assert provider["commands"]["prepare_submission"].startswith("python3 scripts/yao.py world-class-submission-kit"), provider + assert "world-class-intake" in provider["commands"]["validate_intake"], provider + assert "world-class-submission-review" in provider["commands"]["review_queue"], provider + assert "world-class-ledger" in provider["commands"]["refresh_ledger"], provider + assert "world-class-claim-guard" in provider["commands"]["guard_claim"], provider + assert "provider-backed model run" in provider["must_collect"]["provenance_requirements"], provider + assert "reports/output_execution_runs.json summary.model_executed_count > 0" in provider["must_collect"]["success_checks"], provider + provider_source = {item["field"]: item for item in provider["source_checklist"]} + assert provider_source["model_executed_count"]["status"] == "pass", provider_source + assert provider_source["timing_observed_count"]["status"] == "pass", provider_source + assert provider_source["token_observed_count"]["status"] == "pass", provider_source + human = items["human-adjudication"] + human_source = {item["field"]: item for item in human["source_checklist"]} + assert human["observed_state"]["raw_content_allowed"] is False, human + assert human_source["raw_content_allowed"]["status"] == "pass", human_source + markdown = output_md.read_text(encoding="utf-8") + assert "World-Class Operator Runbook" in markdown, markdown + assert "runbook counts as completion: `false`" in markdown, markdown + assert "phase queue counts as completion: `false`" in markdown, markdown + assert "coordination counts as completion: `false`" in markdown, markdown + assert "release gate counts as completion: `false`" in markdown, markdown + assert "## Coordination Plan" in markdown, markdown + assert "`review-and-release-gate`" in markdown, markdown + assert "## Release Gate" in markdown, markdown + assert "world-class-claim-guard" in markdown, markdown + assert "## Phase Queue" in markdown, markdown + assert "| `unblock-access` | `blocked` |" in markdown, markdown + assert "Valid intake means ready for submission review; ledger review still requires passing source evidence." in markdown, markdown + assert "| Evidence | Ledger | Intake | Review | Blocked checks | Next source action | Owner |" in markdown, markdown + assert "| `provider-holdout` | `pending` | `awaiting-submission` | `awaiting-submission` | `0` | none | operator with provider credentials |" in markdown, markdown + assert "Source Runbook" in markdown, markdown + assert "### Phase Queue" in markdown, markdown + assert "--provider-runner openai" in markdown, markdown + assert "--provider-runner deepseek" in markdown, markdown + assert "" not in markdown, markdown + assert "OPENAI_API_KEY=" not in markdown, markdown + assert "### Next Source Actions" in markdown, markdown + assert "| Token usage observed | `10` | `>0` | `pass` |" in markdown, markdown + assert "Source Evidence Snapshot" in markdown, markdown + assert "| Check | Current | Expected | Status | Next action |" in markdown, markdown + assert "| Provider model run | `10` | `>0` | `pass` | Run provider-backed output-exec with real credentials. |" in markdown, markdown + html = output_html.read_text(encoding="utf-8") + assert "World-Class Operator Runbook" in html, html[:400] + assert "ledger and claim guard" in html, html + assert "position:sticky" in html, html + assert "Ready0" in html, html + assert "Invalid0" in html, html + assert f"Queue{summary['phase_queue_blocked_count']}/{summary['phase_queue_count']}" in html, html + assert f"Blocked{summary['source_blocked_count']}" in html, html + assert "
    Blocked
    0
    " in html, html + assert "
    Queue
    1
    " in html, html + assert "Phase Queue" in html, html + assert "Coordination Plan" in html, html + assert "Release Gate" in html, html + assert "review-and-release-gate" in html, html + assert "blocked-until-evidence-accepted" in html, html + assert "Next Source Actions" in html, html + assert "Source Runbook" in html, html + assert "--provider-runner openai" in html, html + assert "--provider-runner deepseek" in html, html + assert "<redacted>" not in html and "" not in html, html + assert "OPENAI_API_KEY=<redacted>" not in html, html + assert "Source Evidence Snapshot" in html, html + assert "model_executed_count" in html, html + assert "model_executed_count: 10 / >0" in html, html + assert "raw_content_allowed: False / false" in html, html + assert "= 6, submitted_summary + assert submitted_summary["phase_queue_count"] == 2, submitted_summary + assert submitted_summary["phase_queue_blocked_count"] == 2, submitted_summary + assert submitted_summary["phase_queue_counts_as_completion"] is False, submitted_summary + assert submitted_summary["ready_to_claim_world_class"] is False, submitted_summary + submitted_provider = {item["evidence_key"]: item for item in submitted["items"]}["provider-holdout"] + assert submitted_provider["ledger_status"] == "accepted", submitted_provider + assert submitted_provider["intake_readiness"] == "ready-for-ledger-review", submitted_provider + assert submitted_provider["review_state"] == "accepted", submitted_provider + assert submitted_provider["source_accepted"] is True, submitted_provider + assert submitted_provider["blocked_source_check_count"] == 0, submitted_provider + assert submitted_provider["phase_queue_blocked_count"] == 0, submitted_provider + assert submitted_provider["next_source_actions"] == [], submitted_provider + assert "tests/tmp_world_class_operator_runbook/valid_submissions" in submitted_provider["commands"]["validate_intake"], submitted_provider + assert "tests/tmp_world_class_operator_runbook/valid_submissions" in submitted_provider["commands"]["review_queue"], submitted_provider + assert "tests/tmp_world_class_operator_runbook/valid_submissions" in submitted_provider["commands"]["refresh_ledger"], submitted_provider + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_preflight.py b/tests/verify_world_class_preflight.py new file mode 100644 index 0000000..ebfd7da --- /dev/null +++ b/tests/verify_world_class_preflight.py @@ -0,0 +1,355 @@ +#!/usr/bin/env python3 +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_world_class_preflight.py" +CLI = ROOT / "scripts" / "yao.py" +TMP = ROOT / "tests" / "tmp_world_class_preflight" + + +def run_preflight(extra_env: dict[str, str] | None = None, *extra: str) -> subprocess.CompletedProcess[str]: + env = dict(os.environ) + env.pop("OPENAI_API_KEY", None) + env.pop("DEEPSEEK_API_KEY", None) + env.pop("YAO_OUTPUT_EVAL_MODEL", None) + if extra_env: + env.update(extra_env) + return subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--generated-at", + "2026-06-16", + *extra, + ], + cwd=ROOT, + capture_output=True, + text=True, + env=env, + check=True, + ) + + +def run_cli(*extra: str) -> subprocess.CompletedProcess[str]: + env = dict(os.environ) + env["YAO_CLI_TELEMETRY"] = "0" + env.pop("OPENAI_API_KEY", None) + env.pop("DEEPSEEK_API_KEY", None) + return subprocess.run( + [sys.executable, str(CLI), "world-class-preflight", str(ROOT), *extra], + cwd=ROOT, + capture_output=True, + text=True, + env=env, + check=True, + ) + + +def by_key(items: list[dict], key: str) -> dict: + return next(item for item in items if item.get("evidence_key") == key) + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + + output_json = TMP / "world_class_evidence_preflight.json" + output_md = TMP / "world_class_evidence_preflight.md" + output_html = TMP / "world_class_evidence_preflight.html" + proc = run_preflight(None, "--output-json", str(output_json), "--output-md", str(output_md), "--output-html", str(output_html)) + payload = json.loads(proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + summary = payload["summary"] + assert summary["decision"] == "collection-preflight-blocked", summary + assert summary["ready_to_claim_world_class"] is False, summary + assert summary["preflight_counts_as_evidence"] is False, summary + assert summary["credential_value_exposed"] is False, summary + assert summary["evidence_item_count"] == 4, summary + assert summary["pending_count"] == 4, summary + assert summary["precheck_count"] >= 12, summary + assert summary["precheck_missing_count"] >= 1, summary + assert summary["precheck_external_required_count"] == 2, summary + assert summary["precheck_human_required_count"] == 1, summary + assert summary["source_check_count"] >= 13, summary + assert summary["source_pass_count"] + summary["source_blocked_count"] == summary["source_check_count"], summary + assert summary["source_blocked_count"] >= 6, summary + assert summary["repair_checklist_count"] == ( + summary["source_blocked_count"] + + summary["precheck_missing_count"] + + summary["precheck_human_required_count"] + + summary["precheck_external_required_count"] + ), summary + assert summary["repair_blocked_count"] == summary["repair_checklist_count"], summary + assert summary["repair_ready_count"] == 0, summary + assert summary["repair_phase_counts"]["unblock-access"] >= 1, summary + assert summary["repair_phase_counts"]["collect-source"] >= 1, summary + assert summary["phase_queue_count"] == 2, summary + assert summary["phase_queue_blocked_count"] == 2, summary + assert summary["phase_queue_row_count"] == summary["repair_checklist_count"], summary + assert summary["phase_queue_next_phase"] == "unblock-access", summary + assert summary["phase_queue_next_action_id"] == "human-adjudication-precheck-human-reviewer", summary + assert summary["phase_queue_next_command"] == ( + "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions" + ), summary + assert summary["phase_queue_counts_as_completion"] is False, summary + assert summary["next_repair_action_id"] == "human-adjudication-precheck-human-reviewer", summary + assert summary["next_repair_phase"] == "unblock-access", summary + assert summary["next_repair_owner"] == "human reviewer", summary + assert summary["next_repair_command"] == ( + "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions" + ), summary + assert summary["repair_counts_as_completion"] is False, summary + assert payload["submissions"]["preflight_counts_submission_as_completion"] is False, payload + assert payload["submissions"]["drafts_count_as_evidence"] is False, payload + assert payload["submissions"]["submission_kit_command"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + "--output-dir evidence/world_class/submissions" + ), payload["submissions"] + assert payload["submissions"]["submission_kit_prefill_command"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + "--output-dir evidence/world_class/submissions --prefill-artifacts" + ), payload["submissions"] + assert payload["submissions"]["artifact_prefill_counts_as_evidence"] is False, payload + phase_queue = payload["phase_queue"] + assert [item["phase"] for item in phase_queue] == ["unblock-access", "collect-source"], phase_queue + assert all(item["counts_as_completion"] is False for item in phase_queue), phase_queue + assert phase_queue[0]["status"] == "blocked", phase_queue + assert phase_queue[0]["blocked_count"] >= 1, phase_queue + assert phase_queue[0]["next_action_id"] == "human-adjudication-precheck-human-reviewer", phase_queue + assert phase_queue[0]["verification_command"] == ( + "python3 scripts/yao.py world-class-preflight . --submissions-dir evidence/world_class/submissions" + ), phase_queue + assert "human reviewer" in phase_queue[0]["owners"], phase_queue + assert "human-adjudication" in phase_queue[0]["evidence_keys"], phase_queue + assert phase_queue[1]["phase"] == "collect-source", phase_queue + assert phase_queue[1]["row_count"] == summary["source_blocked_count"], phase_queue + role_contract = payload["submissions"]["artifact_role_contract"] + assert role_contract["role_source"] == "world-class-submission-kit", role_contract + assert role_contract["counts_as_evidence"] is False, role_contract + assert role_contract["artifact_prefill_counts_as_evidence"] is False, role_contract + assert role_contract["submission_ref_total_count"] >= 4, role_contract + assert role_contract["submission_ref_ready_count"] <= role_contract["submission_ref_total_count"], role_contract + assert role_contract["supporting_evidence_total_count"] >= 4, role_contract + roles = {item["role"]: item for item in role_contract["roles"]} + assert roles["submission-ref"]["copy_to_artifact_refs"] is True, roles + assert roles["supporting-evidence"]["copy_to_artifact_refs"] is False, roles + assert payload["artifacts"]["html"] == "reports/world_class_evidence_preflight.html", payload["artifacts"] + submission_commands = payload["submissions"]["commands"] + assert submission_commands["prepare_submission"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + "--output-dir evidence/world_class/submissions" + ), submission_commands + assert submission_commands["prepare_prefilled_submission"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + "--output-dir evidence/world_class/submissions --prefill-artifacts" + ), submission_commands + assert submission_commands["validate_intake"] == ( + "python3 scripts/yao.py world-class-intake . --submissions-dir evidence/world_class/submissions" + ), submission_commands + assert submission_commands["submission_review"] == ( + "python3 scripts/yao.py world-class-submission-review . --submissions-dir evidence/world_class/submissions" + ), submission_commands + assert submission_commands["refresh_ledger"] == ( + "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions" + ), submission_commands + assert submission_commands["guard_claim"] == "python3 scripts/yao.py world-class-claim-guard .", submission_commands + + provider = by_key(payload["items"], "provider-holdout") + assert provider["status"] == "blocked", provider + assert provider["commands"]["prepare_submission"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + "--evidence-key provider-holdout --output-dir evidence/world_class/submissions" + ), provider + assert provider["commands"]["prepare_prefilled_submission"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + "--evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts" + ), provider + assert provider["submission_kit"]["drafts_count_as_evidence"] is False, provider + assert provider["submission_kit"]["artifact_prefill_counts_as_evidence"] is False, provider + assert provider["submission_kit"]["prefill_command"] == provider["commands"]["prepare_prefilled_submission"], provider + assert provider["submission_kit"]["output_dir"] == "evidence/world_class/submissions", provider + assert provider["submission_kit"]["draft_path"] == "evidence/world_class/submissions/provider-holdout.json", provider + provider_roles = provider["submission_kit"]["artifact_role_contract"] + assert provider_roles["submission_ref_total_count"] == 1, provider_roles + assert provider_roles["submission_ref_ready_count"] == 1, provider_roles + assert provider_roles["supporting_evidence_total_count"] >= 1, provider_roles + provider_checks = {item["key"]: item for item in provider["prechecks"]} + assert provider_checks["provider-api-key"]["status"] == "missing", provider_checks + assert provider_checks["provider-api-key"]["actual"] == "not-set", provider_checks + assert provider_checks["provider-api-key"]["secret_value_redacted"] is True, provider_checks + assert provider_checks["provider-api-key"]["env_any"] == ["OPENAI_API_KEY", "DEEPSEEK_API_KEY"], provider_checks + assert "sk-test-secret" not in proc.stdout, proc.stdout + assert "OPENAI_API_KEY" in proc.stdout, proc.stdout + assert "DEEPSEEK_API_KEY" in proc.stdout, proc.stdout + provider_repairs = {item["target"]: item for item in provider["repair_checklist"]} + provider_phases = {item["phase"]: item for item in provider["phase_queue"]} + assert set(provider_phases) == {"unblock-access"}, provider_phases + assert provider_phases["unblock-access"]["next_action_id"] == "provider-holdout-precheck-provider-api-key", provider_phases + assert provider_repairs["provider-api-key"]["repair_type"] == "precheck", provider_repairs + assert provider_repairs["provider-api-key"]["action_id"] == "provider-holdout-precheck-provider-api-key", provider_repairs + assert provider_repairs["provider-api-key"]["phase"] == "unblock-access", provider_repairs + assert provider_repairs["provider-api-key"]["priority"] == 20, provider_repairs + assert provider_repairs["provider-api-key"]["owner"] == "operator with provider credentials", provider_repairs + assert provider_repairs["provider-api-key"]["verification_command"].endswith( + "world-class-preflight . --submissions-dir evidence/world_class/submissions" + ), provider_repairs + assert provider_repairs["provider-api-key"]["counts_as_completion"] is False, provider_repairs + provider_source = {item["field"]: item for item in provider["source_checklist"]} + assert provider_source["model_executed_count"]["status"] == "pass", provider_source + assert provider_source["timing_observed_count"]["status"] == "pass", provider_source + assert provider_source["token_observed_count"]["status"] == "pass", provider_source + + human = by_key(payload["items"], "human-adjudication") + assert human["status"] == "ready-for-human-review", human + human_checks = {item["key"]: item for item in human["prechecks"]} + assert human_checks["human-reviewer"]["status"] == "human-required", human_checks + assert "reviewer identity" in human["next_action"], human + assert any("Record a reviewer choice and reason" in row["next_action"] for row in human["source_checklist"]), human + assert any("required rationale" in item["next_action"] for item in human["prechecks"]), human + assert any("reviewed_at" in item["next_action"] for item in human["prechecks"]), human + human_repairs = {item["target"]: item for item in human["repair_checklist"]} + assert human_repairs["human-reviewer"]["repair_type"] == "precheck", human_repairs + assert human_repairs["human-reviewer"]["owner"] == "human reviewer", human_repairs + assert human_repairs["pending_count"]["repair_type"] == "source-check", human_repairs + assert "output-review" in human_repairs["pending_count"]["verification_command"], human_repairs + + native = by_key(payload["items"], "native-permission-enforcement") + assert native["status"] == "blocked", native + assert any(item["status"] == "external-required" for item in native["prechecks"]), native + + markdown = output_md.read_text(encoding="utf-8") + assert "World-Class Evidence Preflight" in markdown, markdown + assert "ready to claim world-class: `false`" in markdown, markdown + assert "preflight counts as evidence: `false`" in markdown, markdown + assert "credential value exposed: `false`" in markdown, markdown + assert "Submission Kit Handoff" in markdown, markdown + assert "Phase Queue" in markdown, markdown + assert "Phase queue rows group the same repair checklist into operator execution phases" in markdown, markdown + assert "phase queue: `2` blocked / `2` phases" in markdown, markdown + assert "next phase action: `human-adjudication-precheck-human-reviewer`" in markdown, markdown + assert "| Priority | Phase | Status | Rows | Owners | Evidence | Verify | Next action |" in markdown, markdown + assert "Repair Checklist" in markdown, markdown + assert "Repair rows convert preflight and source blockers into a prioritized operator queue" in markdown, markdown + assert "repair rows:" in markdown, markdown + assert "next repair action: `human-adjudication-precheck-human-reviewer`" in markdown, markdown + assert "| Priority | Phase | Owner | Evidence | Type | Target | Status | Verify | Next action |" in markdown, markdown + assert "`unblock-access`" in markdown, markdown + assert "operator with provider credentials" in markdown, markdown + assert "`provider-api-key`" in markdown, markdown + assert "Provider model run" in markdown, markdown + assert "world-class-submission-kit . --output-dir evidence/world_class/submissions" in markdown, markdown + assert "world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts" in markdown, markdown + assert "world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions" in markdown, markdown + assert "world-class-submission-kit . --evidence-key provider-holdout --output-dir evidence/world_class/submissions --prefill-artifacts" in markdown, markdown + assert "drafts count as evidence: `false`" in markdown, markdown + assert "artifact prefill counts as evidence: `false`" in markdown, markdown + assert "does not make a draft count as evidence" in markdown, markdown + assert "submission refs ready:" in markdown, markdown + assert "`submission-ref` rows are the only checklist rows expected in `artifact_refs`" in markdown, markdown + assert "| `supporting-evidence` | `false` |" in markdown, markdown + assert "values are never printed" in markdown, markdown + html = output_html.read_text(encoding="utf-8") + assert "World-Class Evidence Preflight" in html, html + assert "World-Class Evidence Preflight" in html, html + assert "Phase Queue" in html, html + assert "phase-row blocked" in html, html + assert "
    Rows
    " in html, html + assert "
    Counts
    " in html, html + assert "does not count as completion" in html, html + assert "Evidence Queue" in html, html + assert "Submission Kit" in html, html + assert "Repair Rows" in html, html + assert "Repairs" in html, html + assert "repair-row blocked" in html, html + assert "
    Priority
    " in html, html + assert "
    Phase
    " in html, html + assert "
    Owner
    " in html, html + assert "operator with provider credentials" in html, html + assert "world-class-preflight . --submissions-dir evidence/world_class/submissions" in html, html + assert "provider-api-key" in html, html + assert "Provider model run" in html, html + assert "world-class-submission-kit . --output-dir evidence/world_class/submissions" in html, html + assert "world-class-submission-kit . --output-dir evidence/world_class/submissions --prefill-artifacts" in html, html + assert "provider-holdout" in html, html + assert "Artifact prefill is convenience data only." in html, html + assert "artifact prefill counts as evidence" in html, html + assert "Artifact Roles" in html, html + assert "copy to artifact_refs: true" in html, html + assert "supporting-evidence" in html, html + assert "Source Checks" in html, html + assert "ready_to_claim_world_class" in html, html + assert "Environment variables are displayed only as set or not-set" in html, html + + env_json = TMP / "preflight_with_env.json" + env_proc = run_preflight( + {"DEEPSEEK_API_KEY": "sk-test-secret", "YAO_OUTPUT_EVAL_MODEL": "deepseek-test"}, + "--output-json", + str(env_json), + "--output-md", + str(TMP / "preflight_with_env.md"), + "--output-html", + str(TMP / "preflight_with_env.html"), + ) + env_payload = json.loads(env_proc.stdout) + env_provider = by_key(env_payload["items"], "provider-holdout") + env_provider_checks = {item["key"]: item for item in env_provider["prechecks"]} + assert env_provider_checks["provider-api-key"]["status"] == "pass", env_provider_checks + assert env_provider_checks["provider-api-key"]["actual"] == "set", env_provider_checks + assert env_provider_checks["provider-api-key"]["set_envs"] == ["DEEPSEEK_API_KEY"], env_provider_checks + assert env_provider_checks["provider-model"]["status"] == "pass", env_provider_checks + assert "sk-test-secret" not in env_proc.stdout, env_proc.stdout + assert "sk-test-secret" not in (TMP / "preflight_with_env.html").read_text(encoding="utf-8"), env_payload + assert env_payload["summary"]["credential_value_exposed"] is False, env_payload + assert env_payload["summary"]["ready_to_claim_world_class"] is False, env_payload + assert env_payload["summary"]["repair_checklist_count"] == payload["summary"]["repair_checklist_count"] - 1, env_payload + assert env_payload["summary"]["phase_queue_row_count"] == payload["summary"]["phase_queue_row_count"] - 1, env_payload + assert env_payload["summary"]["phase_queue_count"] == 2, env_payload + + spaced_dir = TMP / "submission kit spaced" + spaced_proc = run_preflight( + None, + "--submissions-dir", + str(spaced_dir), + "--output-json", + str(TMP / "preflight_spaced.json"), + "--output-md", + str(TMP / "preflight_spaced.md"), + "--output-html", + str(TMP / "preflight_spaced.html"), + ) + spaced_payload = json.loads(spaced_proc.stdout) + quoted_spaced = "'tests/tmp_world_class_preflight/submission kit spaced'" + assert quoted_spaced in spaced_payload["submissions"]["commands"]["prepare_submission"], spaced_payload["submissions"] + assert quoted_spaced in spaced_payload["submissions"]["commands"]["prepare_prefilled_submission"], spaced_payload["submissions"] + assert quoted_spaced in by_key(spaced_payload["items"], "provider-holdout")["commands"]["prepare_submission"], spaced_payload["items"] + assert quoted_spaced in by_key(spaced_payload["items"], "provider-holdout")["commands"]["prepare_prefilled_submission"], spaced_payload["items"] + + cli_proc = run_cli( + "--output-json", + str(TMP / "cli_preflight.json"), + "--output-md", + str(TMP / "cli_preflight.md"), + "--output-html", + str(TMP / "cli_preflight.html"), + "--generated-at", + "2026-06-16", + ) + cli_payload = json.loads(cli_proc.stdout) + assert cli_payload["summary"]["decision"] == "collection-preflight-blocked", cli_payload + assert cli_payload["summary"]["preflight_counts_as_evidence"] is False, cli_payload + assert (TMP / "cli_preflight.md").exists(), cli_payload + assert (TMP / "cli_preflight.html").exists(), cli_payload + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_submission_kit.py b/tests/verify_world_class_submission_kit.py new file mode 100644 index 0000000..50bffc1 --- /dev/null +++ b/tests/verify_world_class_submission_kit.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +INTAKE_SCRIPT = ROOT / "scripts" / "render_world_class_evidence_intake.py" +KIT_SCRIPT = ROOT / "scripts" / "prepare_world_class_submission_kit.py" +TMP = ROOT / "tests" / "tmp_world_class_submission_kit" + + +def run_intake(*extra: str) -> dict: + extra_args = list(extra) + if "--output-json" not in extra_args: + extra_args.extend(["--output-json", str(TMP / "last_intake.json")]) + if "--output-md" not in extra_args: + extra_args.extend(["--output-md", str(TMP / "last_intake.md")]) + proc = subprocess.run( + [ + sys.executable, + str(INTAKE_SCRIPT), + str(ROOT), + "--generated-at", + "2026-06-14", + *extra_args, + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + return json.loads(proc.stdout) + + +def run_kit(*extra: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(KIT_SCRIPT), + str(ROOT), + "--generated-at", + "2026-06-14", + *extra, + ], + cwd=ROOT, + capture_output=True, + text=True, + check=True, + ) + + +def assert_documented_submission_commands() -> None: + expected_fragments = [ + 'SUBMISSIONS_DIR="${SUBMISSIONS_DIR:-evidence/world_class/submissions}"', + 'world-class-preflight . --submissions-dir "$SUBMISSIONS_DIR"', + 'world-class-submission-kit . --output-dir "$SUBMISSIONS_DIR"', + 'world-class-submission-kit . --output-dir "$SUBMISSIONS_DIR" --prefill-artifacts', + 'world-class-intake . --submissions-dir "$SUBMISSIONS_DIR"', + 'world-class-submission-review . --submissions-dir "$SUBMISSIONS_DIR"', + 'world-class-ledger . --submissions-dir "$SUBMISSIONS_DIR"', + ] + for relative_path in ("README.md", "evidence/world_class/README.md"): + text = (ROOT / relative_path).read_text(encoding="utf-8") + for fragment in expected_fragments: + assert fragment in text, (relative_path, fragment) + assert "/tmp/yao-world-class-submission-kit" not in text, relative_path + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + assert_documented_submission_commands() + + kit_dir = TMP / "submission_kit" + kit_proc = run_kit("--output-dir", str(kit_dir), "--evidence-key", "provider-holdout") + kit_payload = json.loads(kit_proc.stdout) + assert kit_payload["ok"] is True, kit_payload + assert kit_payload["summary"]["decision"] == "submission-kit-ready", kit_payload["summary"] + assert kit_payload["summary"]["requested_count"] == 1, kit_payload["summary"] + assert kit_payload["summary"]["written_count"] == 1, kit_payload["summary"] + assert kit_payload["summary"]["artifact_checklist_count"] >= 1, kit_payload["summary"] + assert kit_payload["summary"]["artifact_ready_count"] >= 1, kit_payload["summary"] + assert kit_payload["summary"]["submission_ref_count"] == 1, kit_payload["summary"] + assert kit_payload["summary"]["submission_ref_ready_count"] == 1, kit_payload["summary"] + assert kit_payload["summary"]["supporting_artifact_count"] >= 1, kit_payload["summary"] + assert kit_payload["summary"]["supporting_artifact_ready_count"] >= 1, kit_payload["summary"] + assert kit_payload["summary"]["source_check_count"] == 3, kit_payload["summary"] + assert kit_payload["summary"]["source_pass_count"] == 3, kit_payload["summary"] + assert kit_payload["summary"]["source_blocked_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["evidence_matrix_count"] == 1, kit_payload["summary"] + assert kit_payload["summary"]["evidence_matrix_collect_source_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["evidence_matrix_submission_ref_count"] == 1, kit_payload["summary"] + assert kit_payload["summary"]["evidence_matrix_submission_ref_ready_count"] == 1, kit_payload["summary"] + assert kit_payload["summary"]["evidence_matrix_supporting_artifact_count"] >= 1, kit_payload["summary"] + assert kit_payload["summary"]["evidence_matrix_supporting_artifact_ready_count"] >= 1, kit_payload["summary"] + assert kit_payload["summary"]["evidence_matrix_counts_as_completion"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["repair_checklist_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["repair_blocked_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["repair_ready_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["repair_phase_counts"] == {}, kit_payload["summary"] + assert kit_payload["summary"]["phase_queue_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["phase_queue_blocked_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["phase_queue_row_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["phase_queue_next_phase"] == "", kit_payload["summary"] + assert kit_payload["summary"]["phase_queue_next_action_id"] == "", kit_payload["summary"] + assert kit_payload["summary"]["phase_queue_next_command"] == "", kit_payload["summary"] + assert kit_payload["summary"]["phase_queue_counts_as_completion"] is False, kit_payload["summary"] + assert kit_payload["summary"]["next_repair_action_id"] == "", kit_payload["summary"] + assert kit_payload["summary"]["next_repair_phase"] == "", kit_payload["summary"] + assert kit_payload["summary"]["next_repair_owner"] == "", kit_payload["summary"] + assert kit_payload["summary"]["next_repair_command"] == "", kit_payload["summary"] + assert kit_payload["summary"]["repair_counts_as_completion"] is False, kit_payload["summary"] + assert kit_payload["summary"]["handoff_step_count"] == 7, kit_payload["summary"] + assert kit_payload["summary"]["handoff_blocked_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["handoff_fix_required_count"] == 0, kit_payload["summary"] + assert kit_payload["summary"]["handoff_counts_as_completion"] is False, kit_payload["summary"] + assert kit_payload["summary"]["drafts_count_as_evidence"] is False, kit_payload["summary"] + assert kit_payload["safety"]["template_only_drafts"] is True, kit_payload["safety"] + assert kit_payload["safety"]["raw_content_allowed"] is False, kit_payload["safety"] + assert kit_payload["safety"]["operator_handoff_counts_as_evidence"] is False, kit_payload["safety"] + assert "review_submission" in kit_payload["commands"], kit_payload["commands"] + assert kit_payload["files"][0]["output_path"].endswith( + f"tests/{TMP.name}/submission_kit/provider-holdout.json" + ), kit_payload["files"] + + assert len(kit_payload["evidence_matrix"]) == 1, kit_payload["evidence_matrix"] + matrix_row = kit_payload["evidence_matrix"][0] + assert matrix_row["evidence_key"] == "provider-holdout", matrix_row + assert matrix_row["stage"] == "validate-packet", matrix_row + assert matrix_row["draft_status"] == "written", matrix_row + assert matrix_row["artifact_ready_count"] >= 1, matrix_row + assert matrix_row["artifact_total_count"] >= matrix_row["artifact_ready_count"], matrix_row + assert matrix_row["submission_ref_ready_count"] == 1, matrix_row + assert matrix_row["submission_ref_total_count"] == 1, matrix_row + assert matrix_row["supporting_artifact_ready_count"] >= 1, matrix_row + assert matrix_row["supporting_artifact_total_count"] >= matrix_row["supporting_artifact_ready_count"], matrix_row + assert matrix_row["source_pass_count"] == 3, matrix_row + assert matrix_row["source_check_count"] == 3, matrix_row + assert matrix_row["source_blocked_count"] == 0, matrix_row + assert matrix_row["counts_as_completion"] is False, matrix_row + assert "validate intake" in matrix_row["next_action"].lower() or "packet" in matrix_row["next_action"].lower(), matrix_row + + repair_rows = {item["target"]: item for item in kit_payload["repair_checklist"]} + assert repair_rows == {}, repair_rows + + assert kit_payload["phase_queue"] == [], kit_payload["phase_queue"] + + handoff_steps = {item["step_id"]: item for item in kit_payload["operator_handoff"]} + assert list(handoff_steps) == [ + "prepare-drafts", + "collect-source", + "edit-submission", + "validate-intake", + "review-submission", + "refresh-ledger", + "guard-claim", + ], handoff_steps + assert handoff_steps["prepare-drafts"]["status"] == "ready", handoff_steps + assert handoff_steps["collect-source"]["status"] == "ready", handoff_steps + assert handoff_steps["collect-source"]["counts_as_completion"] is False, handoff_steps + assert handoff_steps["collect-source"]["blocking_condition"] == "", handoff_steps + assert "world-class-submission-review" in handoff_steps["review-submission"]["command"], handoff_steps + assert all(item["counts_as_completion"] is False for item in kit_payload["operator_handoff"]), handoff_steps + + artifact_rows = {item["path"]: item for item in kit_payload["artifact_checklist"]} + assert "reports/output_execution_runs.json" in artifact_rows, artifact_rows + assert artifact_rows["reports/output_execution_runs.json"]["artifact_ref_ready"] is True, artifact_rows + assert artifact_rows["reports/output_execution_runs.json"]["artifact_role"] == "submission-ref", artifact_rows + assert artifact_rows["reports/output_execution_runs.json"]["submission_ref_required"] is True, artifact_rows + assert len(artifact_rows["reports/output_execution_runs.json"]["sha256"]) == 64, artifact_rows + assert artifact_rows["reports/output_execution_runs.json"]["contains_raw_content"] is False, artifact_rows + assert artifact_rows["reports/output_execution_runs.md"]["artifact_role"] == "supporting-evidence", artifact_rows + assert artifact_rows["reports/output_execution_runs.md"]["submission_ref_required"] is False, artifact_rows + + source_rows = {item["field"]: item for item in kit_payload["source_checklist"]} + assert source_rows["model_executed_count"]["status"] == "pass", source_rows + assert source_rows["model_executed_count"]["actual"] > 0, source_rows + assert source_rows["timing_observed_count"]["status"] == "pass", source_rows + assert source_rows["token_observed_count"]["status"] == "pass", source_rows + + kit_draft = json.loads((kit_dir / "provider-holdout.json").read_text(encoding="utf-8")) + assert kit_draft["template_only"] is True, kit_draft + assert kit_draft["attestation"]["real_external_or_human_evidence"] is False, kit_draft + assert kit_draft["attestation"]["ledger_reviewer_approved"] is False, kit_draft + assert kit_draft["attestation"]["ledger_reviewer"] == "", kit_draft + assert kit_draft["attestation"]["ledger_reviewed_at"] == "", kit_draft + assert "sha256" not in kit_draft["artifact_refs"][0], kit_draft + assert kit_draft["provenance"]["run_command"] == ( + "python3 scripts/yao.py output-exec --provider-runner --provider-model --timeout-seconds 60" + ), kit_draft + assert kit_draft["provenance"]["credential_env"] == "", kit_draft + assert "" not in json.dumps(kit_draft), kit_draft + + kit_manifest = json.loads((kit_dir / "submission_manifest.json").read_text(encoding="utf-8")) + assert kit_manifest["summary"]["ledger_counts_submission_as_completion"] is False, kit_manifest["summary"] + assert kit_manifest["summary"]["artifact_prefill_enabled"] is False, kit_manifest["summary"] + assert kit_manifest["summary"]["artifact_ref_prefill_count"] == 0, kit_manifest["summary"] + assert kit_manifest["artifact_checklist"] == kit_payload["artifact_checklist"], kit_manifest["artifact_checklist"] + assert kit_manifest["source_checklist"] == kit_payload["source_checklist"], kit_manifest["source_checklist"] + assert kit_manifest["repair_checklist"] == kit_payload["repair_checklist"], kit_manifest["repair_checklist"] + assert kit_manifest["phase_queue"] == kit_payload["phase_queue"], kit_manifest["phase_queue"] + assert kit_manifest["artifacts"]["html"].endswith( + f"tests/{TMP.name}/submission_kit/index.html" + ), kit_manifest["artifacts"] + + kit_readme = (kit_dir / "README.md").read_text(encoding="utf-8") + assert "Drafts are not accepted evidence." in kit_readme, kit_readme + assert "Execution Runbook" in kit_readme, kit_readme + assert "--provider-runner openai" in kit_readme, kit_readme + assert "--provider-runner deepseek" in kit_readme, kit_readme + assert "" not in kit_readme, kit_readme + assert "validate intake" in kit_readme, kit_readme + assert "Artifact Checklist" in kit_readme, kit_readme + assert "Evidence Matrix" in kit_readme, kit_readme + assert "Phase Queue" in kit_readme, kit_readme + assert "This queue groups repair rows by execution phase" in kit_readme, kit_readme + assert "Repair Checklist" in kit_readme, kit_readme + assert "Operator Handoff" in kit_readme, kit_readme + assert "Handoff rows are procedural" in kit_readme, kit_readme + assert "ledger_reviewed_at" in kit_readme, kit_readme + assert "`review-submission`" in kit_readme, kit_readme + assert "Submission refs" in kit_readme, kit_readme + assert "Supporting assets" in kit_readme, kit_readme + assert "`submission-ref`" in kit_readme, kit_readme + assert "`supporting-evidence`" in kit_readme, kit_readme + assert "`collect-source`" in kit_readme, kit_readme + assert "Queue rows are procedural guidance only" in kit_readme, kit_readme + assert "Matrix rows are guidance only" in kit_readme, kit_readme + assert "Repair rows are procedural guidance and do not count as completion evidence." in kit_readme, kit_readme + assert "Source Evidence Snapshot" in kit_readme, kit_readme + assert "Provider model run" in kit_readme, kit_readme + assert "reports/output_execution_runs.json" in kit_readme, kit_readme + assert "Provider model run" in kit_readme, kit_readme + + kit_html = (kit_dir / "index.html").read_text(encoding="utf-8") + assert "World-Class Evidence Submission Kit" in kit_html, kit_html + assert "Drafts are not accepted evidence" in kit_html, kit_html + assert "provider-holdout" in kit_html, kit_html + assert "Artifact Checklist" in kit_html, kit_html + assert "Evidence Matrix" in kit_html, kit_html + assert "Phase Queue" in kit_html, kit_html + assert "No phase queue rows were generated" in kit_html, kit_html + assert "Repair Checklist" in kit_html, kit_html + assert "No repair rows were generated" in kit_html, kit_html + assert "collect-source" in kit_html, kit_html + assert "operator with provider credentials" in kit_html, kit_html + assert "--provider-runner openai" in kit_html, kit_html + assert "--provider-runner deepseek" in kit_html, kit_html + assert "Operator Handoff" in kit_html, kit_html + assert "handoff-card ready" in kit_html, kit_html + assert "does not count as completion" in kit_html, kit_html + assert "matrix-card validate-packet" in kit_html, kit_html + assert ( + f"
    Submission refs
    {matrix_row['submission_ref_ready_count']}/{matrix_row['submission_ref_total_count']} ready
    " + in kit_html + ), kit_html + assert ( + f"
    Supporting assets
    {matrix_row['supporting_artifact_ready_count']}/{matrix_row['supporting_artifact_total_count']} ready
    " + in kit_html + ), kit_html + assert "
    Source
    3/3 pass
    " in kit_html, kit_html + assert "Source Evidence Snapshot" in kit_html, kit_html + assert "
    Field
    model_executed_count
    " in kit_html, kit_html + assert "

    Provider model run

    " in kit_html, kit_html + assert "
    Current
    10
    " in kit_html, kit_html + assert "
    Expected
    >0
    " in kit_html, kit_html + assert "World-Class Evidence Submission Kit" in kit_html, kit_html + assert "Execution Runbook" in kit_html, kit_html + assert "--provider-runner openai" in kit_html, kit_html + assert "--provider-runner deepseek" in kit_html, kit_html + assert "<redacted>" not in kit_html and "" not in kit_html, kit_html + assert "Do not include credentials, raw prompts, raw outputs, transcripts, notes, or private user content." in kit_html, kit_html + assert "Rows marked submission-ref are the paths expected in artifact_refs" in kit_html, kit_html + + prefilled_kit_dir = TMP / "prefilled_submission_kit" + prefilled_proc = run_kit( + "--output-dir", + str(prefilled_kit_dir), + "--evidence-key", + "provider-holdout", + "--prefill-artifacts", + ) + prefilled_payload = json.loads(prefilled_proc.stdout) + assert prefilled_payload["summary"]["artifact_prefill_enabled"] is True, prefilled_payload["summary"] + assert prefilled_payload["summary"]["artifact_ref_prefill_count"] == 1, prefilled_payload["summary"] + assert prefilled_payload["summary"]["artifact_ref_unfilled_count"] == 0, prefilled_payload["summary"] + assert prefilled_payload["summary"]["drafts_count_as_evidence"] is False, prefilled_payload["summary"] + assert prefilled_payload["safety"]["artifact_prefill_counts_as_evidence"] is False, prefilled_payload["safety"] + assert prefilled_payload["files"][0]["prefilled_artifact_ref_count"] == 1, prefilled_payload["files"] + prefilled_draft = json.loads((prefilled_kit_dir / "provider-holdout.json").read_text(encoding="utf-8")) + assert prefilled_draft["template_only"] is True, prefilled_draft + assert prefilled_draft["attestation"]["real_external_or_human_evidence"] is False, prefilled_draft + assert prefilled_draft["attestation"]["ledger_reviewer_approved"] is False, prefilled_draft + assert len(prefilled_draft["artifact_refs"][0]["sha256"]) == 64, prefilled_draft + assert prefilled_draft["artifact_refs"][0]["contains_raw_content"] is False, prefilled_draft + prefilled_readme = (prefilled_kit_dir / "README.md").read_text(encoding="utf-8") + assert "Optional artifact prefill only inserts SHA-256 digests" in prefilled_readme, prefilled_readme + assert "does not mark a draft as real evidence" in prefilled_readme, prefilled_readme + prefilled_html = (prefilled_kit_dir / "index.html").read_text(encoding="utf-8") + assert "artifact prefill only inserts local SHA-256 digests" in prefilled_html, prefilled_html + assert "
    Prefill
    1 artifact refs
    " in prefilled_html, prefilled_html + + native_kit_dir = TMP / "native_permission_kit" + native_kit_proc = run_kit("--output-dir", str(native_kit_dir), "--evidence-key", "native-permission-enforcement") + native_kit_payload = json.loads(native_kit_proc.stdout) + native_rows = {item["path"]: item for item in native_kit_payload["artifact_checklist"]} + assert native_kit_payload["summary"]["artifact_glob_expansion_count"] >= 1, native_kit_payload["summary"] + if "dist/targets/openai/adapter.json" in native_rows: + assert native_rows["dist/targets/openai/adapter.json"]["source_pattern"] == "dist/targets/*/adapter.json", native_rows + assert native_rows["dist/targets/openai/adapter.json"]["artifact_ref_ready"] is True, native_rows + assert len(native_rows["dist/targets/openai/adapter.json"]["sha256"]) == 64, native_rows + else: + glob_rows = [ + item for item in native_kit_payload["artifact_checklist"] + if item["source_pattern"] == "dist/targets/*/adapter.json" + ] + assert len(glob_rows) == 1, glob_rows + assert glob_rows[0]["status"] == "glob-no-match", glob_rows[0] + assert glob_rows[0]["artifact_ref_ready"] is False, glob_rows[0] + assert glob_rows[0]["concrete_reference_required"] is True, glob_rows[0] + native_readme = (native_kit_dir / "README.md").read_text(encoding="utf-8") + assert "Glob patterns are expanded into concrete files" in native_readme, native_readme + + draft_intake = run_intake("--submissions-dir", str(kit_dir)) + assert draft_intake["ok"] is False, draft_intake + assert draft_intake["summary"]["submission_count"] == 1, draft_intake["summary"] + assert draft_intake["summary"]["invalid_submission_count"] == 1, draft_intake["summary"] + assert draft_intake["submissions"][0]["evidence_key"] == "provider-holdout", draft_intake["submissions"] + assert all(item["evidence_key"] != "unknown" for item in draft_intake["submissions"]), draft_intake["submissions"] + + spaced_kit_dir = TMP / "submission kit spaced" + spaced_kit_proc = run_kit("--output-dir", str(spaced_kit_dir), "--evidence-key", "provider-holdout") + spaced_kit_payload = json.loads(spaced_kit_proc.stdout) + quoted_spaced_dir = f"'tests/{TMP.name}/submission kit spaced'" + assert quoted_spaced_dir in spaced_kit_payload["commands"]["validate_intake"], spaced_kit_payload["commands"] + assert quoted_spaced_dir in spaced_kit_payload["commands"]["refresh_ledger"], spaced_kit_payload["commands"] + spaced_readme = (spaced_kit_dir / "README.md").read_text(encoding="utf-8") + assert quoted_spaced_dir in spaced_readme, spaced_readme + + existing_proc = run_kit("--output-dir", str(kit_dir), "--evidence-key", "provider-holdout") + existing_payload = json.loads(existing_proc.stdout) + assert existing_payload["summary"]["existing_count"] == 1, existing_payload["summary"] + assert existing_payload["files"][0]["status"] == "exists", existing_payload["files"] + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_world_class_submission_review.py b/tests/verify_world_class_submission_review.py new file mode 100644 index 0000000..26943f1 --- /dev/null +++ b/tests/verify_world_class_submission_review.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +import hashlib +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "render_world_class_submission_review.py" +TMP = ROOT / "tests" / "tmp_world_class_submission_review" + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def provider_submission(*, valid: bool = True) -> dict: + return { + "schema_version": "1.0", + "evidence_key": "provider-holdout", + "template_only": False, + "category": "external", + "source_type": "provider-output-eval", + "submitted_by": "Yao provider operator", + "submitted_at": "2026-06-14", + "summary": "Aggregate provider-backed holdout evidence for ledger review.", + "artifact_refs": [ + { + "path": "reports/output_execution_runs.json", + "kind": "aggregate-report", + "contains_raw_content": not valid, + "sha256": sha256_file(ROOT / "reports" / "output_execution_runs.json") if valid else "example-only", + } + ], + "provenance": { + "provider": "deepseek", + "model": "deepseek-v4-flash", + "credential_material_committed": False, + }, + "privacy": { + "raw_user_content_included": False, + "raw_provider_prompt_included": False, + "credentials_included": False, + "secrets_included": False, + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "attestation": { + "real_external_or_human_evidence": valid, + "reviewer_or_operator_identity_present": valid, + "artifact_refs_reviewed": valid, + "privacy_contract_satisfied": valid, + "ledger_reviewer_approved": valid, + "ledger_reviewer": "Yao ledger reviewer" if valid else "", + "ledger_reviewed_at": "2026-06-14" if valid else "", + }, + } + + +def run_review(*extra: str, check: bool = True) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(SCRIPT), + str(ROOT), + "--generated-at", + "2026-06-14", + *extra, + ], + cwd=ROOT, + capture_output=True, + text=True, + check=check, + ) + + +def main() -> None: + shutil.rmtree(TMP, ignore_errors=True) + TMP.mkdir(parents=True, exist_ok=True) + empty_submissions = TMP / "empty-submissions" + empty_submissions.mkdir() + + output_json = TMP / "world_class_submission_review.json" + output_md = TMP / "world_class_submission_review.md" + default_proc = run_review( + "--output-json", + str(output_json), + "--output-md", + str(output_md), + "--submissions-dir", + str(empty_submissions), + ) + payload = json.loads(default_proc.stdout) + assert payload["schema_version"] == "1.0", payload + assert payload["ok"] is True, payload + summary = payload["summary"] + assert payload["report_contract"]["contract"] == "world-class-submission-review", payload + assert payload["report_contract"]["top_level_mirrors_summary"] is True, payload + for field in payload["report_contract"]["summary_fields"]: + assert payload[field] == summary[field], (field, payload[field], summary[field]) + assert summary["decision"] == "awaiting-submissions", summary + assert summary["review_item_count"] == 4, summary + assert summary["awaiting_submission_count"] == 4, summary + assert summary["ready_for_ledger_review_count"] == 0, summary + assert summary["valid_packet_source_incomplete_count"] == 0, summary + assert summary["source_check_count"] >= 13, summary + assert summary["source_pass_count"] + summary["source_blocked_count"] == summary["source_check_count"], summary + assert summary["source_blocked_count"] >= 6, summary + assert summary["review_counts_submission_as_completion"] is False, summary + assert payload["ready_to_claim_world_class"] is False, payload + assert payload["review_item_count"] == 4, payload + assert payload["awaiting_submission_count"] == 4, payload + assert payload["source_blocked_count"] == summary["source_blocked_count"], payload + provider_item = {item["evidence_key"]: item for item in payload["items"]}["provider-holdout"] + assert provider_item["review_state"] == "awaiting-submission", provider_item + assert provider_item["submission_status"] == "missing", provider_item + assert provider_item["source_accepted"] is True, provider_item + provider_source = {item["field"]: item for item in provider_item["source_checklist"]} + assert provider_source["model_executed_count"]["status"] == "pass", provider_source + assert provider_source["timing_observed_count"]["status"] == "pass", provider_source + assert provider_source["token_observed_count"]["status"] == "pass", provider_source + human_item = {item["evidence_key"]: item for item in payload["items"]}["human-adjudication"] + human_source = {item["field"]: item for item in human_item["source_checklist"]} + assert human_item["observed_state"]["raw_content_allowed"] is False, human_item + assert human_item["observed_state"]["raw_content_path_count"] == 0, human_item + assert human_source["raw_content_allowed"]["status"] == "pass", human_source + assert "prompt_sha256" in " ".join(human_item["success_checks"]), human_item + assert "prompt_sha256" in " ".join(human_item["privacy_contract"]), human_item + markdown = output_md.read_text(encoding="utf-8") + assert "World-Class Submission Review" in markdown, markdown + assert "review counts submission as completion: `false`" in markdown, markdown + assert "Provider model run: 10 / >0 => pass" in markdown, markdown + assert "`provider-holdout`" in markdown, markdown + + submissions = TMP / "valid_submissions" + submissions.mkdir() + (submissions / "provider-holdout.json").write_text( + json.dumps(provider_submission(valid=True), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + # The kit manifest must not be treated as an evidence packet. + (submissions / "submission_manifest.json").write_text( + json.dumps({"schema_version": "1.0", "not": "an evidence packet"}, ensure_ascii=False) + "\n", + encoding="utf-8", + ) + submitted_proc = run_review( + "--submissions-dir", + str(submissions), + "--output-json", + str(TMP / "submitted_review.json"), + "--output-md", + str(TMP / "submitted_review.md"), + check=False, + ) + assert submitted_proc.returncode == 0, submitted_proc.stdout + submitted_payload = json.loads(submitted_proc.stdout) + submitted_summary = submitted_payload["summary"] + assert submitted_payload["ok"] is True, submitted_payload + assert submitted_summary["decision"] == "awaiting-submissions", submitted_summary + assert submitted_summary["valid_packet_source_incomplete_count"] == 0, submitted_summary + assert submitted_summary["awaiting_submission_count"] == 3, submitted_summary + assert submitted_summary["invalid_submission_count"] == 0, submitted_summary + assert submitted_summary["accepted_count"] == 1, submitted_summary + assert submitted_summary["source_pass_count"] + submitted_summary["source_blocked_count"] == submitted_summary["source_check_count"], submitted_summary + assert submitted_summary["source_blocked_count"] >= 6, submitted_summary + submitted_provider = {item["evidence_key"]: item for item in submitted_payload["items"]}["provider-holdout"] + assert submitted_provider["review_state"] == "accepted", submitted_provider + assert submitted_provider["intake_status"] == "pass", submitted_provider + assert submitted_provider["submission_status"] == "submitted", submitted_provider + assert submitted_provider["artifact_ref_count"] == 1, submitted_provider + assert submitted_provider["source_accepted"] is True, submitted_provider + assert submitted_provider["intake_errors"] == [], submitted_provider + assert "model_executed_count" in submitted_provider["observed_state"], submitted_provider + submitted_source = {item["field"]: item for item in submitted_provider["source_checklist"]} + assert submitted_source["model_executed_count"]["status"] == "pass", submitted_source + assert submitted_source["timing_observed_count"]["status"] == "pass", submitted_source + + invalid_dir = TMP / "invalid_submissions" + invalid_dir.mkdir() + (invalid_dir / "provider-holdout.json").write_text( + json.dumps(provider_submission(valid=False), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + invalid_proc = run_review( + "--submissions-dir", + str(invalid_dir), + "--output-json", + str(TMP / "invalid_review.json"), + "--output-md", + str(TMP / "invalid_review.md"), + check=False, + ) + assert invalid_proc.returncode == 2, invalid_proc.stdout + invalid_payload = json.loads(invalid_proc.stdout) + assert invalid_payload["ok"] is False, invalid_payload + assert invalid_payload["summary"]["decision"] == "fix-submissions", invalid_payload["summary"] + invalid_provider = {item["evidence_key"]: item for item in invalid_payload["items"]}["provider-holdout"] + assert invalid_provider["review_state"] == "fix-submission", invalid_provider + assert invalid_provider["intake_errors"], invalid_provider + assert any("sha256" in error for error in invalid_provider["intake_errors"]), invalid_provider + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/verify_yao_cli.py b/tests/verify_yao_cli.py new file mode 100644 index 0000000..9bd902e --- /dev/null +++ b/tests/verify_yao_cli.py @@ -0,0 +1,700 @@ +#!/usr/bin/env python3 +import json +import subprocess +import sys +from pathlib import Path + +from yao_cli_helpers import ( + BENCHMARK_FIXTURE_DIR, + ROOT, + assert_cli_module_contracts, + assert_created_skill_reports, + assert_creation_report_view, + assert_help_surface, + refresh_root_report_consistency_inputs, + run, + run_with_env, +) + + +def main() -> None: + tmp_root = ROOT / "tests" / "tmp_cli" + if tmp_root.exists(): + subprocess.run(["rm", "-rf", str(tmp_root)], check=True) + tmp_root.mkdir(parents=True, exist_ok=True) + remote_version = tmp_root / "remote-version.txt" + remote_version.write_text("9.9.9\n", encoding="utf-8") + assert_cli_module_contracts() + assert_help_surface() + + init_result = run("init", "cli-demo-skill", "--description", "CLI demo skill.", "--output-dir", str(tmp_root)) + assert init_result["ok"], init_result + created = Path(init_result["payload"]["root"]) + assert (created / "SKILL.md").exists(), created + assert (created / "README.md").exists(), created + assert_created_skill_reports(created) + assert "Honest Boundaries" in (created / "SKILL.md").read_text(encoding="utf-8"), created + assert_creation_report_view(init_result["payload"]["report_view"]) + init_skill_ir = init_result["payload"]["skill_ir"] + assert init_skill_ir["name"] == "cli-demo-skill", init_skill_ir + assert init_skill_ir["trigger_samples"] >= 1, init_skill_ir + + telemetry_log = tmp_root / "cli-telemetry-events.jsonl" + telemetry_env = { + "YAO_CLI_TELEMETRY": "1", + "YAO_CLI_TELEMETRY_EVENTS": str(telemetry_log), + } + telemetry_ok = run_with_env(telemetry_env, "validate", str(created)) + assert telemetry_ok["ok"], telemetry_ok + telemetry_fail = run_with_env( + telemetry_env, + "output-exec", + "--runner-command", + json.dumps([sys.executable, str(ROOT / "scripts" / "local_output_eval_runner.py")]), + "--provider-runner", + "openai", + ) + assert not telemetry_fail["ok"], telemetry_fail + telemetry_events = [json.loads(line) for line in telemetry_log.read_text(encoding="utf-8").splitlines()] + assert len(telemetry_events) == 2, telemetry_events + assert telemetry_events[0]["event"] == "script_run", telemetry_events + assert telemetry_events[0]["source"] == "yao_cli", telemetry_events + assert telemetry_events[0]["command"] == "validate", telemetry_events + assert telemetry_events[0]["outcome"] == "accepted", telemetry_events + assert telemetry_events[0]["failure_type"] == "none", telemetry_events + assert telemetry_events[1]["command"] == "output-exec", telemetry_events + assert telemetry_events[1]["outcome"] == "failed", telemetry_events + assert telemetry_events[1]["failure_type"] == "script_error", telemetry_events + + skill_os2_audit_result = run( + "skill-os2-audit", + str(ROOT), + "--output-json", + str(tmp_root / "skill_os2_audit.json"), + "--output-md", + str(tmp_root / "skill_os2_audit.md"), + "--generated-at", + "2026-06-13", + ) + assert skill_os2_audit_result["ok"], skill_os2_audit_result + assert skill_os2_audit_result["payload"]["summary"]["decision"] == "continue-iteration", skill_os2_audit_result + assert skill_os2_audit_result["payload"]["summary"]["world_class_ready"] is False, skill_os2_audit_result + + skill_os2_coverage_result = run( + "skill-os2-coverage", + str(ROOT), + "--output-json", + str(tmp_root / "skill_os2_coverage.json"), + "--output-md", + str(tmp_root / "skill_os2_coverage.md"), + "--generated-at", + "2026-06-14", + ) + assert skill_os2_coverage_result["ok"], skill_os2_coverage_result + assert skill_os2_coverage_result["payload"]["summary"]["local_blueprint_ready"] is True, skill_os2_coverage_result + assert skill_os2_coverage_result["payload"]["summary"]["public_world_class_ready"] is False, skill_os2_coverage_result + + python_compat_result = run( + "python-compat", + str(ROOT), + "--output-json", + str(tmp_root / "python_compatibility.json"), + "--output-md", + str(tmp_root / "python_compatibility.md"), + "--generated-at", + "2026-06-14", + ) + assert python_compat_result["ok"], python_compat_result + assert python_compat_result["payload"]["summary"]["target_python"] == "3.11", python_compat_result + assert python_compat_result["payload"]["summary"]["issue_count"] == 0, python_compat_result + assert python_compat_result["payload"]["summary"]["file_count"] >= 50, python_compat_result + + architecture_result = run( + "architecture-audit", + str(ROOT), + "--output-json", + str(tmp_root / "architecture_maintainability.json"), + "--output-md", + str(tmp_root / "architecture_maintainability.md"), + "--generated-at", + "2026-06-14", + ) + assert architecture_result["ok"], architecture_result + assert architecture_result["payload"]["summary"]["hotspot_count"] == 0, architecture_result + assert architecture_result["payload"]["summary"]["watchlist_count"] == 0, architecture_result + assert architecture_result["payload"]["summary"]["blocker_count"] == 0, architecture_result + assert architecture_result["payload"]["summary"]["command_handler_count"] >= 60, architecture_result + assert architecture_result["payload"]["summary"]["entrypoint_command_handler_count"] < 30, architecture_result + refresh_root_report_consistency_inputs(run, ROOT) + + evidence_consistency_result = run( + "evidence-consistency", str(ROOT), + "--output-json", str(tmp_root / "evidence_consistency.json"), + "--output-md", str(tmp_root / "evidence_consistency.md"), + "--generated-at", "2026-06-15", + ) + assert evidence_consistency_result["ok"], evidence_consistency_result + assert evidence_consistency_result["payload"]["summary"]["decision"] == "consistent", evidence_consistency_result + + quickstart_result = run( + "quickstart", + "--output-dir", + str(tmp_root), + "--github-fixture-dir", + str(BENCHMARK_FIXTURE_DIR), + "--no-update-check", + input_text=( + "quickstart-skill\n" + "Turn messy release notes into a reusable release brief skill.\n" + "release notes, changelog snippets\n" + "A reusable markdown release brief.\n" + "looks right\n" + "It should not publish blog posts or send email.\n" + "consistency, portability\n" + "production\n" + "production\n" + "\n" + "privacy and naming\n" + ), + ) + assert quickstart_result["ok"], quickstart_result + quickstart_root = Path(quickstart_result["payload"]["root"]) + quickstart_reports = [ + "review-viewer.html", "skill-interpretation.html", "skill-interpretation.json", "review-studio.html", + "github-benchmark-scan.md", "intent-confidence.md", "reference-synthesis.md", + "artifact-design-profile.md", "prompt-quality-profile.md", "system-model.md", "compiled_targets.md", + "compiled_targets.json", "adoption_drift_report.md", "review_waivers.md", "review_annotations.md", + ] + assert all((quickstart_root / "reports" / path).exists() for path in quickstart_reports), quickstart_root + assert quickstart_result["payload"]["archetype"] == "production", quickstart_result + assert quickstart_result["payload"]["guidance"]["experience_note"], quickstart_result + assert quickstart_result["payload"]["guidance"]["problem_diagnosis"]["candidates"], quickstart_result + assert quickstart_result["payload"]["intent_confidence"]["score"] >= 70, quickstart_result + assert quickstart_result["payload"]["recommendation"]["summary"], quickstart_result + assert quickstart_result["payload"]["reference_mode"]["mode"] == "silent", quickstart_result + quickstart_report_view = quickstart_result["payload"]["report_view"] + assert quickstart_report_view["html_report"].endswith("reports/skill-overview.html"), quickstart_report_view + assert quickstart_report_view["interpretation_report"].endswith("reports/skill-interpretation.html"), quickstart_report_view + assert Path(quickstart_report_view["html_report"]).exists(), quickstart_report_view + assert Path(quickstart_report_view["interpretation_report"]).exists(), quickstart_report_view + assert Path(quickstart_report_view["review_studio"]).exists(), quickstart_report_view + assert "Skill 已创建完成" in quickstart_report_view["message"], quickstart_report_view + assert "默认使用中文简体" in quickstart_report_view["message"], quickstart_report_view + assert quickstart_result["payload"]["guidance"]["next_steps"][0].startswith("Open reports/skill-interpretation.html"), quickstart_result + assert quickstart_result["payload"]["guidance"]["next_steps"][1].startswith("Open reports/skill-overview.html"), quickstart_result + assert "reports/review-studio.html" in quickstart_result["payload"]["guidance"]["next_steps"][3], quickstart_result + assert "interpretation report" in quickstart_result["payload"]["guidance"]["next_steps"][0], quickstart_result + evidence_artifacts = quickstart_result["payload"]["reviewer_evidence"]["artifacts"] + assert evidence_artifacts["reference_synthesis"].endswith("reports/reference-synthesis.md"), quickstart_result + assert evidence_artifacts["prompt_quality_profile"].endswith("reports/prompt-quality-profile.md"), quickstart_result + assert evidence_artifacts["system_model"].endswith("reports/system-model.md"), quickstart_result + assert evidence_artifacts["skill_interpretation"].endswith("reports/skill-interpretation.html"), quickstart_result + assert evidence_artifacts["review_studio"].endswith("reports/review-studio.html"), quickstart_result + assert "uncertainty_or_conflict" not in quickstart_result["payload"], quickstart_result + quickstart_manifest = json.loads((quickstart_root / "manifest.json").read_text(encoding="utf-8")) + assert quickstart_manifest["status"] == "active", quickstart_manifest + assert quickstart_manifest["lifecycle_stage"] == "production", quickstart_manifest + quickstart_validate_result = run("validate", str(quickstart_root), "--require-manifest") + assert quickstart_validate_result["ok"], quickstart_validate_result + + quickstart_conflict_result = run( + "quickstart", + "--output-dir", + str(tmp_root), + "--github-fixture-dir", + str(BENCHMARK_FIXTURE_DIR), + "--no-update-check", + input_text=( + "quickstart-conflict-skill\n" + "Turn repeated release notes into a governed release command skill.\n" + "release notes, changelog snippets\n" + "A governed release packet.\n" + "looks right\n" + "It should not publish blog posts or send email.\n" + "auditability, portability\n" + "governed\n" + "governed\n" + "Minimal vibe helper::taste::Keep the first pass fast, minimal, and lightweight.::Do not add review, governance, or approval steps.\n" + "privacy and naming\n" + ), + ) + assert quickstart_conflict_result["ok"], quickstart_conflict_result + assert quickstart_conflict_result["payload"]["reference_mode"]["mode"] == "explicit", quickstart_conflict_result + assert quickstart_conflict_result["payload"]["uncertainty_or_conflict"]["conflicts"], quickstart_conflict_result + + validate_result = run("validate", str(created)) + assert validate_result["ok"], validate_result + assert len(validate_result["payload"]["steps"]) == 4, validate_result + + skill_report_result = run("skill-report", str(created)) + assert skill_report_result["ok"], skill_report_result + assert skill_report_result["payload"]["artifacts"]["html"].endswith("reports/skill-overview.html"), skill_report_result + + skill_interpretation_result = run("skill-interpretation", str(created)) + assert skill_interpretation_result["ok"], skill_interpretation_result + assert skill_interpretation_result["payload"]["artifacts"]["html"].endswith( + "reports/skill-interpretation.html" + ), skill_interpretation_result + assert skill_interpretation_result["payload"]["summary"]["report_kind"] == "skill-interpretation", skill_interpretation_result + assert skill_interpretation_result["payload"]["summary"]["default_language"] == "zh-CN", skill_interpretation_result + + review_viewer_result = run("review-viewer", str(created)) + assert review_viewer_result["ok"], review_viewer_result + assert review_viewer_result["payload"]["artifacts"]["html"].endswith("reports/review-viewer.html"), review_viewer_result + + review_studio_result = run("review-studio", str(created)) + assert review_studio_result["ok"], review_studio_result + assert review_studio_result["payload"]["artifacts"]["html"].endswith("reports/review-studio.html"), review_studio_result + assert review_studio_result["payload"]["summary"]["gate_count"] == 16, review_studio_result + created_world_class_gate = next(item for item in review_studio_result["payload"]["gates"] if item["key"] == "world-class-evidence") + assert created_world_class_gate["status"] == "pass", created_world_class_gate + assert "optional" in created_world_class_gate["detail"], created_world_class_gate + created_architecture_gate = next( + item for item in review_studio_result["payload"]["gates"] if item["key"] == "architecture-maintainability" + ) + assert created_architecture_gate["status"] == "pass", created_architecture_gate + assert "optional" in created_architecture_gate["detail"], created_architecture_gate + + review_waivers_result = run( + "review-waivers", + str(created), + "--add-waiver", + "--gate-key", + "trust-report", + "--reviewer", + "Yao Team", + "--reason", + "Trust warning accepted for this CLI demo with bounded release follow-up.", + "--expires-at", + "2026-09-30", + "--generated-at", + "2026-06-13", + ) + assert review_waivers_result["ok"], review_waivers_result + assert review_waivers_result["payload"]["summary"]["active_count"] == 1, review_waivers_result + assert "trust-report" in review_waivers_result["payload"]["summary"]["covered_gate_keys"], review_waivers_result + + review_annotations_result = run( + "review-annotations", + str(created), + "--add-annotation", + "--annotation-id", + "ann-cli-trigger", + "--gate-key", + "trigger-lab", + "--target-path", + "SKILL.md", + "--line", + "1", + "--severity", + "note", + "--reviewer", + "Yao QA", + "--created-at", + "2026-06-13", + "--body", + "Check trigger wording before reuse.", + ) + assert review_annotations_result["ok"], review_annotations_result + assert review_annotations_result["payload"]["summary"]["annotation_count"] == 1, review_annotations_result + assert (created / "reports" / "review_annotations.md").exists(), review_annotations_result + + registry_result = run( + "registry-audit", + str(ROOT), + "--registry-dir", + str(tmp_root / "registry"), + "--output-json", + str(tmp_root / "registry_audit.json"), + "--output-md", + str(tmp_root / "registry_audit.md"), + "--generated-at", + "2026-06-13", + ) + assert registry_result["ok"], registry_result + assert registry_result["payload"]["package"]["name"] == "yao-meta-skill", registry_result + assert registry_result["payload"]["package"]["checksums"]["package_sha256"], registry_result + + reference_scan_result = run( + "reference-scan", + str(created), + "--external-reference", + "World Class Method::method::Borrow the smallest repeatable evaluation loop.::Do not copy heavy ceremony.", + "--user-reference", + "Product I Admire::taste::Learn the calm structure and clarity of output.::Do not copy wording.", + "--local-constraint", + "Local Naming::structure::Keep folder naming aligned with the local library.::Do not inherit private references.", + ) + assert reference_scan_result["ok"], reference_scan_result + assert reference_scan_result["payload"]["artifacts"]["markdown"].endswith("reports/reference-scan.md"), reference_scan_result + assert len(reference_scan_result["payload"]["summary"]["user_references"]) == 1, reference_scan_result + + github_benchmark_result = run( + "github-benchmark-scan", + str(created), + "--query", + "workflow evaluation portability", + "--fixture-dir", + str(BENCHMARK_FIXTURE_DIR), + ) + assert github_benchmark_result["ok"], github_benchmark_result + assert len(github_benchmark_result["payload"]["repositories"]) == 3, github_benchmark_result + + intent_confidence_result = run("intent-confidence", str(created)) + assert intent_confidence_result["ok"], intent_confidence_result + assert intent_confidence_result["payload"]["summary"]["score"] >= 0, intent_confidence_result + + intent_result = run("intent-dialogue", str(created)) + assert intent_result["ok"], intent_result + assert intent_result["payload"]["artifacts"]["markdown"].endswith("reports/intent-dialogue.md"), intent_result + + reference_synthesis_result = run("reference-synthesis", str(created)) + assert reference_synthesis_result["ok"], reference_synthesis_result + assert reference_synthesis_result["payload"]["artifacts"]["markdown"].endswith("reports/reference-synthesis.md"), reference_synthesis_result + + output_risk_result = run("output-risk-profile", str(created)) + assert output_risk_result["ok"], output_risk_result + assert output_risk_result["payload"]["artifacts"]["markdown"].endswith("reports/output-risk-profile.md"), output_risk_result + assert output_risk_result["payload"]["summary"]["risk_families"], output_risk_result + + artifact_design_result = run("artifact-design-profile", str(created)) + assert artifact_design_result["ok"], artifact_design_result + assert artifact_design_result["payload"]["artifacts"]["markdown"].endswith("reports/artifact-design-profile.md"), artifact_design_result + assert artifact_design_result["payload"]["summary"]["quality_gates"], artifact_design_result + + prompt_quality_result = run("prompt-quality-profile", str(created)) + assert prompt_quality_result["ok"], prompt_quality_result + assert prompt_quality_result["payload"]["artifacts"]["markdown"].endswith("reports/prompt-quality-profile.md"), prompt_quality_result + assert prompt_quality_result["payload"]["summary"]["quality_matrix"], prompt_quality_result + + system_model_result = run("system-model", str(created)) + assert system_model_result["ok"], system_model_result + assert system_model_result["payload"]["artifacts"]["markdown"].endswith("reports/system-model.md"), system_model_result + assert system_model_result["payload"]["summary"]["stability"]["score"] >= 0, system_model_result + + directions_result = run("iteration-directions", str(created)) + assert directions_result["ok"], directions_result + assert directions_result["payload"]["artifacts"]["markdown"].endswith("reports/iteration-directions.md"), directions_result + + skill_ir_result = run("skill-ir", str(created)) + assert skill_ir_result["ok"], skill_ir_result + assert skill_ir_result["payload"]["artifacts"]["json"].endswith("reports/skill-ir.json"), skill_ir_result + created_skill_ir = json.loads((created / "reports" / "skill-ir.json").read_text(encoding="utf-8")) + assert created_skill_ir["schema_version"] == "2.0.0", created_skill_ir + assert created_skill_ir["trigger_surface"]["description"], created_skill_ir + + compile_result = run("compile-skill", str(created), "--target", "openai", "--target", "claude", "--target", "generic", "--target", "vscode") + assert compile_result["ok"], compile_result + assert compile_result["payload"]["summary"]["target_count"] == 4, compile_result + assert compile_result["payload"]["summary"]["block_count"] == 0, compile_result + assert compile_result["payload"]["artifacts"]["markdown"].endswith("reports/compiled_targets.md"), compile_result + + output_eval_result = run( + "output-eval", + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(created / "reports" / "output_quality_scorecard.json"), + "--output-md", + str(created / "reports" / "output_quality_scorecard.md"), + "--blind-pack-json", + str(created / "reports" / "output_blind_review_pack.json"), + "--blind-pack-md", + str(created / "reports" / "output_blind_review_pack.md"), + "--blind-answer-key-json", + str(created / "reports" / "output_blind_answer_key.json"), + ) + assert output_eval_result["ok"], output_eval_result + assert output_eval_result["payload"]["summary"]["with_skill_pass_rate"] > output_eval_result["payload"]["summary"]["baseline_pass_rate"], output_eval_result + assert output_eval_result["payload"]["summary"]["blind_pair_count"] == 5, output_eval_result + assert (created / "reports" / "output_blind_review_pack.md").exists(), output_eval_result + assert (created / "reports" / "output_blind_answer_key.json").exists(), output_eval_result + + output_exec_result = run( + "output-exec", + "--cases", + str(ROOT / "evals" / "output" / "cases.jsonl"), + "--output-json", + str(created / "reports" / "output_execution_runs.json"), + "--output-md", + str(created / "reports" / "output_execution_runs.md"), + ) + assert output_exec_result["ok"], output_exec_result + assert output_exec_result["payload"]["summary"]["variant_run_count"] == 10, output_exec_result + assert output_exec_result["payload"]["summary"]["recorded_fixture_count"] == 10, output_exec_result + assert (created / "reports" / "output_execution_runs.md").exists(), output_exec_result + + output_exec_conflict = run( + "output-exec", + "--runner-command", + json.dumps([sys.executable, str(ROOT / "scripts" / "local_output_eval_runner.py")]), + "--provider-runner", + "openai", + ) + assert not output_exec_conflict["ok"], output_exec_conflict + assert "Use either --runner-command or --provider-runner" in output_exec_conflict["payload"]["failures"][0], output_exec_conflict + + output_review_result = run( + "output-review", + "--blind-pack", + str(created / "reports" / "output_blind_review_pack.json"), + "--answer-key", + str(created / "reports" / "output_blind_answer_key.json"), + "--decisions", + str(created / "reports" / "output_review_decisions.json"), + "--output-json", + str(created / "reports" / "output_review_adjudication.json"), + "--output-md", + str(created / "reports" / "output_review_adjudication.md"), + ) + assert output_review_result["ok"], output_review_result + assert output_review_result["payload"]["summary"]["judgment_count"] == 0, output_review_result + assert output_review_result["payload"]["summary"]["pending_count"] == 5, output_review_result + assert output_review_result["payload"]["summary"]["reviewer_checklist_count"] == 5, output_review_result + assert output_review_result["payload"]["summary"]["reviewer_checklist_pending_count"] == 5, output_review_result + assert all(not item["answer_key_visible"] for item in output_review_result["payload"]["reviewer_checklist"]), output_review_result + assert (created / "reports" / "output_review_adjudication.md").exists(), output_review_result + + conformance_result = run("conformance", str(created)) + assert conformance_result["ok"], conformance_result + assert conformance_result["payload"]["summary"]["target_count"] == 5, conformance_result + assert conformance_result["payload"]["artifacts"]["markdown"].endswith("reports/conformance_matrix.md"), conformance_result + + trust_result = run("trust", str(created)) + assert trust_result["ok"], trust_result + assert trust_result["payload"]["summary"]["secret_findings"] == 0, trust_result + assert trust_result["payload"]["artifacts"]["markdown"].endswith("reports/security_trust_report.md"), trust_result + + atlas_result = run( + "skill-atlas", + "--workspace-root", + str(tmp_root), + "--output-dir", + str(tmp_root / "skill_atlas"), + "--report-html", + str(tmp_root / "skill_atlas.html"), + "--report-json", + str(tmp_root / "skill_atlas.json"), + "--today", + "2026-06-13", + ) + assert atlas_result["ok"], atlas_result + assert atlas_result["payload"]["summary"]["skill_count"] >= 2, atlas_result + assert atlas_result["payload"]["artifacts"]["report_html"].endswith("skill_atlas.html"), atlas_result + + feedback_result = run( + "feedback", + str(created), + "--note", + "Keep the first version light and tighten exclusions before adding scripts.", + "--rating", + "4", + "--category", + "boundary", + "--recommended-action", + "tighten-trigger", + ) + assert feedback_result["ok"], feedback_result + assert feedback_result["payload"]["feedback"]["summary"]["count"] == 1, feedback_result + + adoption_drift_result = run( + "adoption-drift", + str(created), + "--record-event", + "skill_activation", + "--activation-type", + "explicit", + "--outcome", + "accepted", + "--timestamp", + "2026-06-13T10:00:00Z", + ) + assert adoption_drift_result["ok"], adoption_drift_result + assert adoption_drift_result["payload"]["summary"]["event_count"] == 1, adoption_drift_result + assert adoption_drift_result["payload"]["artifacts"]["markdown"].endswith( + "reports/adoption_drift_report.md" + ), adoption_drift_result + + optimize_result = run("optimize-description", "--target", "root") + assert optimize_result["ok"], optimize_result + assert optimize_result["payload"]["winner"]["label"] == "Current", optimize_result + + baseline_compare_result = run("baseline-compare") + assert baseline_compare_result["ok"], baseline_compare_result + assert baseline_compare_result["payload"]["summary"]["target_count"] == 3, baseline_compare_result + + promote_result = run("promote-check") + assert promote_result["ok"], promote_result + assert promote_result["payload"]["summary"]["blocked"] == 0, promote_result + review_result = run("review", "--target", "root") + assert review_result["ok"], review_result + assert review_result["payload"]["artifacts"]["review_md"].endswith("reports/iteration_bundles/yao-meta-skill/review.md") + + snapshot_result = run("release-snapshot", "--target", "root", "--label", "cli-smoke") + assert snapshot_result["ok"], snapshot_result + assert snapshot_result["payload"]["artifacts"]["snapshot_json"].endswith("cli-smoke.json"), snapshot_result + + flow_result = run("workspace-flow", "--target", "root", "--label", "cli-flow") + assert flow_result["ok"], flow_result + assert flow_result["payload"]["artifacts"][0]["snapshot"]["artifacts"]["snapshot_md"].endswith("cli-flow.md"), flow_result + + report_result = run("report") + assert report_result["ok"], report_result + assert "iteration_ledger" in report_result["payload"]["artifacts"], report_result + assert "portability_score" in report_result["payload"]["artifacts"], report_result + assert "python_compatibility" in report_result["payload"]["artifacts"], report_result + assert "architecture_maintainability" in report_result["payload"]["artifacts"], report_result + assert "artifact_design_profile" in report_result["payload"]["artifacts"], report_result + assert "prompt_quality_profile" in report_result["payload"]["artifacts"], report_result + assert "compiled_targets" in report_result["payload"]["artifacts"], report_result + assert "output_execution" in report_result["payload"]["artifacts"], report_result + assert "output_review_kit" in report_result["payload"]["artifacts"], report_result + assert "output_review_adjudication" in report_result["payload"]["artifacts"], report_result + assert "adoption_drift" in report_result["payload"]["artifacts"], report_result + assert "review_annotations" in report_result["payload"]["artifacts"], report_result + assert "world_class_evidence_plan" in report_result["payload"]["artifacts"], report_result + assert "world_class_evidence_ledger" in report_result["payload"]["artifacts"], report_result + assert "world_class_evidence_intake" in report_result["payload"]["artifacts"], report_result + assert "world_class_claim_guard" in report_result["payload"]["artifacts"], report_result + assert "benchmark_reproducibility" in report_result["payload"]["artifacts"], report_result + assert "evidence_consistency" in report_result["payload"]["artifacts"], report_result + assert all(key in report_result["payload"]["artifacts"] for key in ("skill_os2_audit", "skill_os2_coverage")), report_result + assert any(step["command"].startswith("render_skill_os2_audit.py ") and step["ok"] for step in report_result["payload"]["steps"]), report_result + assert "weekly_curator" in report_result["payload"]["artifacts"], report_result + assert report_result["payload"]["artifacts"]["skill_overview"] == "reports/skill-overview.json", report_result + assert report_result["payload"]["artifacts"]["skill_interpretation"] == "reports/skill-interpretation.json", report_result + assert report_result["payload"]["artifacts"]["skill_interpretation_html"] == "reports/skill-interpretation.html", report_result + assert (report_result["payload"]["artifacts"]["review_studio"], report_result["payload"]["artifacts"]["review_studio_html"]) == ("reports/review-studio.json", "reports/review-studio.html"), report_result + assert report_result["payload"]["artifacts"]["review_viewer"] == "reports/review-viewer.json", report_result + assert report_result["payload"]["artifacts"]["review_viewer_html"] == "reports/review-viewer.html", report_result + report_output_execution = json.loads((ROOT / "reports" / "output_execution_runs.json").read_text(encoding="utf-8")) + assert report_output_execution["summary"]["command_executed_count"] == 10, report_output_execution + assert report_output_execution["summary"]["recorded_fixture_count"] == 0, report_output_execution + if report_output_execution["summary"]["model_executed_count"] > 0: + assert report_output_execution["summary"]["token_observed_count"] == 10, report_output_execution + assert report_output_execution["summary"]["token_estimated_count"] == 0, report_output_execution + else: + assert report_output_execution["summary"]["model_executed_count"] == 0, report_output_execution + + package_dir = tmp_root / "dist" + package_result = run("package", ".", "--platform", "generic", "--output-dir", str(package_dir)) + assert package_result["ok"], package_result + assert (package_dir / "targets" / "generic" / "adapter.json").exists(), package_dir + generic_adapter = json.loads((package_dir / "targets" / "generic" / "adapter.json").read_text(encoding="utf-8")) + assert generic_adapter["compiler"]["name"] == "yao-skill-ir-compiler", generic_adapter + assert generic_adapter["compiled_contract"]["target"] == "generic", generic_adapter + + package_zip_dir = tmp_root / "dist-zip" + package_zip_result = run( + "package", + ".", + "--platform", + "openai", + "--platform", + "claude", + "--platform", + "generic", + "--platform", + "vscode", + "--expectations", + str(ROOT / "evals" / "packaging_expectations.json"), + "--output-dir", + str(package_zip_dir), + "--zip", + ) + assert package_zip_result["ok"], package_zip_result + package_verify_result = run( + "package-verify", + ".", + "--package-dir", + str(package_zip_dir), + "--expectations", + str(ROOT / "evals" / "packaging_expectations.json"), + "--registry-json", + str(ROOT / "reports" / "registry_audit.json"), + "--output-json", + str(tmp_root / "package_verification.json"), + "--output-md", + str(tmp_root / "package_verification.md"), + "--require-zip", + "--generated-at", + "2026-06-13", + ) + assert package_verify_result["ok"], package_verify_result + assert package_verify_result["payload"]["summary"]["adapter_count"] == 4, package_verify_result + assert package_verify_result["payload"]["summary"]["archive_sha256"], package_verify_result + + install_simulate_result = run( + "install-simulate", + ".", + "--package-dir", + str(package_zip_dir), + "--install-root", + str(tmp_root / "install-root"), + "--output-json", + str(tmp_root / "install_simulation.json"), + "--output-md", + str(tmp_root / "install_simulation.md"), + "--generated-at", + "2026-06-13", + ) + assert install_simulate_result["ok"], install_simulate_result + assert install_simulate_result["payload"]["summary"]["archive_extracted"], install_simulate_result + assert install_simulate_result["payload"]["summary"]["adapter_count"] == 4, install_simulate_result + + runtime_permissions_result = run( + "runtime-permissions", + ".", + "--package-dir", + str(package_zip_dir), + "--install-simulation-json", + str(tmp_root / "install_simulation.json"), + "--output-json", + str(tmp_root / "runtime_permission_probes_with_install.json"), + "--output-md", + str(tmp_root / "runtime_permission_probes.md"), + ) + assert runtime_permissions_result["ok"], runtime_permissions_result + runtime_install_summary = runtime_permissions_result["payload"]["summary"] + assert runtime_install_summary["metadata_fallback_count"] == 4, runtime_install_summary + assert runtime_install_summary["native_enforcement_count"] == 0, runtime_install_summary + assert runtime_install_summary["installer_enforcement_pass_count"] == 4, runtime_install_summary + assert runtime_install_summary["installer_permission_failure_count"] == 0, runtime_install_summary + + upgrade_result = run( + "upgrade-check", + ".", + "--previous-package-json", + str(ROOT / "registry" / "examples" / "yao-meta-skill-1.0.0.json"), + "--current-package-json", + str(ROOT / "reports" / "registry_audit.json"), + "--output-json", + str(tmp_root / "upgrade_check.json"), + "--output-md", + str(tmp_root / "upgrade_check.md"), + "--generated-at", + "2026-06-13", + ) + assert upgrade_result["ok"], upgrade_result + assert upgrade_result["payload"]["summary"]["recommended_bump"] == "minor", upgrade_result + + update_result = run( + "check-update", + "--force", + "--no-cache", + "--version-url", + remote_version.as_uri(), + "--manifest-url", + remote_version.as_uri(), + ) + assert not update_result["ok"], update_result + assert update_result["returncode"] == 2, update_result + assert "Update URL scheme is not allowed: file" in update_result["payload"]["error"], update_result + + test_result = run("test", "--target", "promotion-check") + assert test_result["ok"], test_result + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + +if __name__ == "__main__": + main() diff --git a/tests/verify_yao_cli_world_class.py b/tests/verify_yao_cli_world_class.py new file mode 100644 index 0000000..fe34c3a --- /dev/null +++ b/tests/verify_yao_cli_world_class.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +import json +import os +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" +TMP = ROOT / "tests" / "tmp_yao_cli_world_class" + + +def run(*args: str) -> dict: + env = dict(os.environ) + env["YAO_CLI_TELEMETRY"] = "0" + env.pop("YAO_CLI_TELEMETRY_EVENTS", None) + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + env=env, + ) + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stderr": proc.stderr, + } + + +def main() -> None: + if TMP.exists(): + subprocess.run(["rm", "-rf", str(TMP)], check=True) + TMP.mkdir(parents=True, exist_ok=True) + empty_submissions = TMP / "empty_submissions" + empty_submissions.mkdir(parents=True, exist_ok=True) + + world_class_evidence_result = run( + "world-class-evidence", + str(ROOT), + "--output-json", + str(TMP / "world_class_evidence_plan.json"), + "--output-md", + str(TMP / "world_class_evidence_plan.md"), + "--generated-at", + "2026-06-13", + ) + assert world_class_evidence_result["ok"], world_class_evidence_result + assert world_class_evidence_result["payload"]["summary"]["decision"] == "collect-external-evidence", world_class_evidence_result + assert world_class_evidence_result["payload"]["summary"]["ready_to_claim_world_class"] is False, world_class_evidence_result + + world_class_ledger_result = run( + "world-class-ledger", + str(ROOT), + "--output-json", + str(TMP / "world_class_evidence_ledger.json"), + "--output-md", + str(TMP / "world_class_evidence_ledger.md"), + "--submissions-dir", + str(empty_submissions), + "--generated-at", + "2026-06-13", + ) + assert world_class_ledger_result["ok"], world_class_ledger_result + ledger_payload = world_class_ledger_result["payload"] + assert ledger_payload["report_contract"]["top_level_mirrors_summary"] is True, world_class_ledger_result + assert ledger_payload["pending_count"] == ledger_payload["summary"]["pending_count"] == 4, world_class_ledger_result + assert ledger_payload["summary"]["missing_submission_count"] == 4, world_class_ledger_result + assert ledger_payload["summary"]["submitted_entry_count"] == 0, world_class_ledger_result + + world_class_intake_result = run( + "world-class-intake", + str(ROOT), + "--submissions-dir", + str(empty_submissions), + "--output-json", + str(TMP / "world_class_evidence_intake.json"), + "--output-md", + str(TMP / "world_class_evidence_intake.md"), + "--generated-at", + "2026-06-14", + ) + assert world_class_intake_result["ok"], world_class_intake_result + intake_summary = world_class_intake_result["payload"]["summary"] + assert intake_summary["decision"] == "awaiting-submissions", world_class_intake_result + assert intake_summary["template_pass_count"] == 4, world_class_intake_result + assert intake_summary["operator_checklist_count"] == 4, world_class_intake_result + assert intake_summary["operator_checklist_ready_count"] == 0, world_class_intake_result + assert intake_summary["ready_to_claim_world_class"] is False, world_class_intake_result + provider_checklist = next( + item for item in world_class_intake_result["payload"]["operator_checklist"] if item["evidence_key"] == "provider-holdout" + ) + assert provider_checklist["readiness"] == "awaiting-submission", provider_checklist + empty_submissions_arg = str(empty_submissions.relative_to(ROOT)) + assert provider_checklist["commands"]["prepare_submission"] == ( + "python3 scripts/yao.py world-class-submission-kit . " + f"--evidence-key provider-holdout --output-dir {empty_submissions_arg}" + ), provider_checklist + assert world_class_ledger_result["payload"]["summary"]["ready_to_claim_world_class"] is False, world_class_ledger_result + + world_class_submission_kit_result = run( + "world-class-submission-kit", + str(ROOT), + "--output-dir", + str(TMP / "world_class_submission_kit"), + "--evidence-key", + "provider-holdout", + "--generated-at", + "2026-06-14", + "--output-html", + str(TMP / "world_class_submission_kit.html"), + ) + kit_payload = world_class_submission_kit_result["payload"] + assert world_class_submission_kit_result["ok"], world_class_submission_kit_result + assert kit_payload["summary"]["decision"] == "submission-kit-ready", world_class_submission_kit_result + assert kit_payload["summary"]["written_count"] == 1, world_class_submission_kit_result + assert kit_payload["summary"]["drafts_count_as_evidence"] is False, world_class_submission_kit_result + assert kit_payload["summary"]["repair_checklist_count"] == 0, world_class_submission_kit_result + assert kit_payload["summary"]["repair_counts_as_completion"] is False, world_class_submission_kit_result + assert kit_payload["repair_checklist"] == [], world_class_submission_kit_result + assert kit_payload["artifacts"]["html"].endswith("tests/tmp_yao_cli_world_class/world_class_submission_kit.html"), world_class_submission_kit_result + assert (TMP / "world_class_submission_kit" / "provider-holdout.json").exists(), world_class_submission_kit_result + assert (TMP / "world_class_submission_kit" / "submission_manifest.json").exists(), world_class_submission_kit_result + assert (TMP / "world_class_submission_kit.html").exists(), world_class_submission_kit_result + + world_class_submission_review_result = run( + "world-class-submission-review", + str(ROOT), + "--submissions-dir", + str(TMP / "world_class_submission_kit"), + "--output-json", + str(TMP / "world_class_submission_review.json"), + "--output-md", + str(TMP / "world_class_submission_review.md"), + "--generated-at", + "2026-06-14", + ) + assert not world_class_submission_review_result["ok"], world_class_submission_review_result + assert world_class_submission_review_result["returncode"] == 2, world_class_submission_review_result + review_payload = world_class_submission_review_result["payload"] + assert review_payload["report_contract"]["top_level_mirrors_summary"] is True, world_class_submission_review_result + assert review_payload["summary"]["decision"] == "fix-submissions", world_class_submission_review_result + assert review_payload["invalid_submission_count"] == review_payload["summary"]["invalid_submission_count"] == 1, world_class_submission_review_result + assert review_payload["items"][0]["review_state"] == "fix-submission", world_class_submission_review_result + assert (TMP / "world_class_submission_review.md").exists(), world_class_submission_review_result + + world_class_claim_guard_result = run( + "world-class-claim-guard", + str(ROOT), + "--output-json", + str(TMP / "world_class_claim_guard.json"), + "--output-md", + str(TMP / "world_class_claim_guard.md"), + "--generated-at", + "2026-06-14", + ) + assert world_class_claim_guard_result["ok"], world_class_claim_guard_result + assert world_class_claim_guard_result["payload"]["summary"]["decision"] == "claim-guard-pass-evidence-pending", world_class_claim_guard_result + assert world_class_claim_guard_result["payload"]["summary"]["violation_count"] == 0, world_class_claim_guard_result + assert world_class_claim_guard_result["payload"]["summary"]["ledger_pending_count"] == 4, world_class_claim_guard_result + + benchmark_reproducibility_result = run( + "benchmark-reproducibility", + str(ROOT), + "--output-json", + str(TMP / "benchmark_reproducibility.json"), + "--output-md", + str(TMP / "benchmark_reproducibility.md"), + "--generated-at", + "2026-06-13", + ) + assert benchmark_reproducibility_result["ok"], benchmark_reproducibility_result + assert benchmark_reproducibility_result["payload"]["summary"]["reproducibility_ready"] is True, benchmark_reproducibility_result + assert benchmark_reproducibility_result["payload"]["summary"]["world_class_ready"] is False, benchmark_reproducibility_result + + print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/tests/world_class_human_fixtures.py b/tests/world_class_human_fixtures.py new file mode 100644 index 0000000..492d093 --- /dev/null +++ b/tests/world_class_human_fixtures.py @@ -0,0 +1,364 @@ +#!/usr/bin/env python3 +import hashlib +import json +from pathlib import Path + +from world_class_evidence_contract import validate_payload + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_world_class_evidence_intake" + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def write_json(path: Path, payload: dict) -> 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 fixture_review_integrity() -> dict: + return { + "blind_pack_sha256": "f" * 64, + "case_count": 2, + "case_ids": ["case-a", "case-b"], + "prompt_sha256_by_case": { + "case-a": "a" * 64, + "case-b": "b" * 64, + }, + } + + +def fixture_reviewer_attestation() -> dict: + return { + "blind_review_completed_before_answer_key": True, + "answer_key_not_opened_before_decisions": True, + "raw_content_excluded": True, + "reviewer_reason_required": True, + } + + +def artifact_blind_pack_sha256(skill_root: Path) -> str: + path = skill_root / "reports" / "output_review_adjudication.json" + if not path.exists(): + return fixture_review_integrity()["blind_pack_sha256"] + payload = json.loads(path.read_text(encoding="utf-8")) + integrity = payload.get("review_integrity", {}) if isinstance(payload, dict) else {} + return str(integrity.get("blind_pack_sha256", fixture_review_integrity()["blind_pack_sha256"])) + + +def human_submission(skill_root: Path, *, reviewer: str = "Yao QA") -> dict: + blind_pack_sha256 = artifact_blind_pack_sha256(skill_root) + return { + "schema_version": "1.0", + "evidence_key": "human-adjudication", + "template_only": False, + "category": "human", + "source_type": "blind-ab-review", + "submitted_by": reviewer, + "submitted_at": "2026-06-14", + "summary": "Completed blind A/B reviewer decisions for ledger review.", + "artifact_refs": [ + { + "path": "reports/output_review_adjudication.json", + "kind": "adjudication-report", + "contains_raw_content": False, + "sha256": sha256_file(skill_root / "reports" / "output_review_adjudication.json"), + }, + { + "path": "reports/output_review_decisions.json", + "kind": "review-decisions", + "contains_raw_content": False, + "sha256": sha256_file(skill_root / "reports" / "output_review_decisions.json"), + }, + ], + "provenance": { + "reviewer": reviewer, + "reviewed_at": "2026-06-14", + "blind_pack_path": "reports/output_blind_review_pack.md", + "blind_pack_sha256": blind_pack_sha256, + "answer_key_opened_after_decisions": True, + "blind_review_completed_before_answer_key": True, + "decision_fields": ["case_id", "winner_variant", "confidence", "reason", "reviewer_attestation", "review_integrity"], + "reviewer_reason_required": True, + }, + "privacy": { + "raw_user_content_included": False, + "raw_provider_prompt_included": False, + "credentials_included": False, + "secrets_included": False, + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "attestation": { + "real_external_or_human_evidence": True, + "reviewer_or_operator_identity_present": True, + "artifact_refs_reviewed": True, + "privacy_contract_satisfied": True, + "ledger_reviewer_approved": True, + "ledger_reviewer": "Yao ledger reviewer", + "ledger_reviewed_at": "2026-06-14", + }, + } + + +def human_pairs(*, complete: bool) -> list[dict]: + if complete: + return [ + { + "case_id": "case-a", + "status": "match", + "expected_revealed": True, + "reviewer_winner_variant": "A", + "confidence": 0.9, + "reason": "Variant A follows the rubric.", + }, + { + "case_id": "case-b", + "status": "disagree", + "expected_revealed": True, + "reviewer_winner_variant": "B", + "confidence": 0.8, + "reason": "Variant B handles the boundary.", + }, + ] + return [ + { + "case_id": "case-a", + "status": "match", + "expected_revealed": True, + "reviewer_winner_variant": "A", + "confidence": 0.9, + "reason": "Variant A follows the rubric.", + }, + { + "case_id": "case-b", + "status": "pending", + "expected_revealed": False, + "reviewer_winner_variant": "", + "confidence": None, + "reason": "", + }, + ] + + +def write_human_artifacts(skill_root: Path, *, complete: bool = True, reviewer: str = "Yao QA") -> None: + reviewed_at = "2026-06-14" + summary = { + "pair_count": 2, + "judgment_count": 2 if complete else 1, + "pending_count": 0 if complete else 1, + "agreement_count": 2 if complete else 1, + "disagreement_count": 0, + "invalid_decision_count": 0, + "answer_revealed_count": 2 if complete else 1, + "pending_answer_hidden_count": 0 if complete else 1, + "agreement_rate": 100.0, + "needs_review": not complete, + "failure_count": 0, + "reviewer_metadata_present": complete, + "reason_required": True, + "blind_review_attested": complete, + "blind_review_completed_before_answer_key": complete, + "answer_key_not_opened_before_decisions": complete, + "raw_content_excluded_attested": complete, + "reviewer_reason_required_attested": complete, + "ready_for_human_evidence": complete, + "reviewer_checklist_count": 2, + "reviewer_checklist_ready_count": 2 if complete else 1, + } + decisions = [ + {"case_id": "case-a", "winner_variant": "A", "confidence": 0.9, "reason": "Variant A follows the rubric."}, + { + "case_id": "case-b", + "winner_variant": "B" if complete else "", + "confidence": 0.8 if complete else None, + "reason": "Variant B handles the boundary." if complete else "", + }, + ] + write_json( + skill_root / "reports" / "output_review_adjudication.json", + { + "schema_version": "1.0", + "ok": True, + "summary": summary, + "reviewer": reviewer, + "reviewed_at": reviewed_at, + "review_integrity": fixture_review_integrity(), + "reviewer_attestation": fixture_reviewer_attestation() if complete else {}, + "pairs": human_pairs(complete=complete), + "failures": [], + }, + ) + write_json( + skill_root / "reports" / "output_review_decisions.json", + { + "schema_version": "1.0", + "reviewer": reviewer, + "reviewed_at": reviewed_at, + "review_integrity": fixture_review_integrity(), + "reviewer_attestation": fixture_reviewer_attestation() if complete else {}, + "decisions": decisions, + }, + ) + + +def assert_human_submission_error(skill_root: Path, entry: dict, expected_error: str) -> None: + result = validate_payload( + human_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert result["status"] == "fail", result + assert any(expected_error in error for error in result["errors"]), result["errors"] + + +def assert_human_contract_artifact_validation() -> None: + entry = {"key": "human-adjudication", "category": "human"} + skill_root = TMP / "human_contract_root" + write_human_artifacts(skill_root, complete=True) + valid_result = validate_payload( + human_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert valid_result["status"] == "pass", valid_result + assert valid_result["artifact_integrity"]["required_artifact_verified_count"] == 2, valid_result + + wrong_kind_submission = human_submission(skill_root) + wrong_kind_submission["artifact_refs"][1]["kind"] = "aggregate-report" + wrong_kind_result = validate_payload( + wrong_kind_submission, + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert wrong_kind_result["status"] == "fail", wrong_kind_result + assert any("kind must be review-decisions" in error for error in wrong_kind_result["errors"]), wrong_kind_result["errors"] + + write_human_artifacts(skill_root, complete=False) + pending_result = validate_payload( + human_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert pending_result["status"] == "fail", pending_result + assert any("summary.pending_count must be 0" in error for error in pending_result["errors"]), pending_result["errors"] + assert any("summary.judgment_count must equal" in error for error in pending_result["errors"]), pending_result["errors"] + assert any("summary.needs_review must be false" in error for error in pending_result["errors"]), pending_result["errors"] + assert any("A/B winner_variant" in error for error in pending_result["errors"]), pending_result["errors"] + + write_human_artifacts(skill_root, complete=True, reviewer="Yao QA") + mismatch_result = validate_payload( + human_submission(skill_root, reviewer="Different Reviewer"), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert mismatch_result["status"] == "fail", mismatch_result + assert any("provenance.reviewer must match decisions.reviewer" in error for error in mismatch_result["errors"]), mismatch_result["errors"] + + reviewed_at_mismatch = human_submission(skill_root, reviewer="Yao QA") + reviewed_at_mismatch["provenance"]["reviewed_at"] = "2026-06-15" + reviewed_at_result = validate_payload( + reviewed_at_mismatch, + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert reviewed_at_result["status"] == "fail", reviewed_at_result + assert any("provenance.reviewed_at must match decisions.reviewed_at" in error for error in reviewed_at_result["errors"]), ( + reviewed_at_result["errors"] + ) + + answer_key_order = human_submission(skill_root, reviewer="Yao QA") + answer_key_order["provenance"]["answer_key_opened_after_decisions"] = False + answer_key_result = validate_payload( + answer_key_order, + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert answer_key_result["status"] == "fail", answer_key_result + assert any("answer_key_opened_after_decisions" in error for error in answer_key_result["errors"]), answer_key_result["errors"] + + missing_reason_contract = human_submission(skill_root, reviewer="Yao QA") + missing_reason_contract["provenance"]["reviewer_reason_required"] = False + missing_reason_contract["provenance"]["decision_fields"] = ["case_id", "winner_variant", "confidence"] + reason_contract_result = validate_payload( + missing_reason_contract, + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "human-adjudication.json", + root=skill_root, + template_expected=False, + ) + assert reason_contract_result["status"] == "fail", reason_contract_result + assert any("reviewer_reason_required" in error for error in reason_contract_result["errors"]), reason_contract_result["errors"] + assert any("decision_fields must include" in error for error in reason_contract_result["errors"]), reason_contract_result["errors"] + + forged_adjudication = json.loads((skill_root / "reports" / "output_review_adjudication.json").read_text(encoding="utf-8")) + forged_adjudication["summary"]["ready_for_human_evidence"] = False + forged_adjudication["summary"]["reviewer_metadata_present"] = False + forged_adjudication["summary"]["reason_required"] = False + write_json(skill_root / "reports" / "output_review_adjudication.json", forged_adjudication) + assert_human_submission_error(skill_root, entry, "summary.ready_for_human_evidence must be true") + assert_human_submission_error(skill_root, entry, "summary.reviewer_metadata_present must be true") + assert_human_submission_error(skill_root, entry, "summary.reason_required must be true") + + write_human_artifacts(skill_root, complete=True, reviewer="Yao QA") + raw_adjudication = json.loads((skill_root / "reports" / "output_review_adjudication.json").read_text(encoding="utf-8")) + raw_adjudication["pairs"][0]["prompt"] = "verbatim blind review prompt must not be accepted" + raw_adjudication["pairs"][0]["messages"] = ["raw reviewer transcript must not be accepted"] + write_json(skill_root / "reports" / "output_review_adjudication.json", raw_adjudication) + assert_human_submission_error(skill_root, entry, "output_review_adjudication.pairs[0].prompt") + assert_human_submission_error(skill_root, entry, "output_review_adjudication.pairs[0].messages") + + write_human_artifacts(skill_root, complete=True, reviewer="Yao QA") + forged_decisions = json.loads((skill_root / "reports" / "output_review_decisions.json").read_text(encoding="utf-8")) + forged_decisions["decisions"][1]["case_id"] = "unknown-case" + write_json(skill_root / "reports" / "output_review_decisions.json", forged_decisions) + assert_human_submission_error(skill_root, entry, "case_id set must match adjudication pairs") + + write_human_artifacts(skill_root, complete=True, reviewer="Yao QA") + forged_decisions = json.loads((skill_root / "reports" / "output_review_decisions.json").read_text(encoding="utf-8")) + forged_decisions["decisions"][0]["confidence"] = 1.2 + write_json(skill_root / "reports" / "output_review_decisions.json", forged_decisions) + assert_human_submission_error(skill_root, entry, "confidence must be between 0 and 1") + + write_human_artifacts(skill_root, complete=True, reviewer="Yao QA") + forged_decisions = json.loads((skill_root / "reports" / "output_review_decisions.json").read_text(encoding="utf-8")) + forged_decisions["decisions"][0]["expected_winner_variant"] = "A" + write_json(skill_root / "reports" / "output_review_decisions.json", forged_decisions) + assert_human_submission_error(skill_root, entry, "answer-key fields") + + write_human_artifacts(skill_root, complete=True, reviewer="Yao QA") + forged_decisions = json.loads((skill_root / "reports" / "output_review_decisions.json").read_text(encoding="utf-8")) + forged_decisions["decisions"][0]["metadata"] = {"raw_output": "verbatim model answer must not ship"} + write_json(skill_root / "reports" / "output_review_decisions.json", forged_decisions) + assert_human_submission_error(skill_root, entry, "decisions[1].metadata.raw_output") + + write_human_artifacts(skill_root, complete=True, reviewer="Yao QA") + forged_decisions = json.loads((skill_root / "reports" / "output_review_decisions.json").read_text(encoding="utf-8")) + forged_decisions["decisions"][0]["metadata"] = {"API_KEY": "credential material must not ship"} + write_json(skill_root / "reports" / "output_review_decisions.json", forged_decisions) + assert_human_submission_error(skill_root, entry, "decisions[1].metadata.API_KEY") diff --git a/tests/world_class_native_telemetry_fixtures.py b/tests/world_class_native_telemetry_fixtures.py new file mode 100644 index 0000000..129ffc3 --- /dev/null +++ b/tests/world_class_native_telemetry_fixtures.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +import hashlib +import json +from pathlib import Path + +from world_class_evidence_contract import validate_payload + + +ROOT = Path(__file__).resolve().parent.parent +TMP = ROOT / "tests" / "tmp_world_class_evidence_intake" + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def write_json(path: Path, payload: dict) -> 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 native_telemetry_submission(skill_root: Path) -> dict: + return { + "schema_version": "1.0", + "evidence_key": "native-client-telemetry", + "template_only": False, + "category": "external", + "source_type": "native-client-telemetry", + "submitted_by": "Yao client integrator", + "submitted_at": "2026-06-14", + "summary": "Completed native-client-telemetry evidence for ledger review.", + "artifact_refs": [ + { + "path": "reports/adoption_drift_report.json", + "kind": "adoption-drift-report", + "contains_raw_content": False, + "sha256": sha256_file(skill_root / "reports" / "adoption_drift_report.json"), + }, + { + "path": "reports/telemetry_hook_recipes.json", + "kind": "hook-recipes", + "contains_raw_content": False, + "sha256": sha256_file(skill_root / "reports" / "telemetry_hook_recipes.json"), + }, + ], + "provenance": { + "client": "Chrome extension production build", + "native_host_manifest": "/Users/laoyao/.config/chrome/native-hosts/yao-meta-skill.json", + "event_source": "external", + "metadata_only": True, + }, + "privacy": { + "raw_user_content_included": False, + "raw_provider_prompt_included": False, + "credentials_included": False, + "secrets_included": False, + }, + "anti_overclaim": { + "planned_work_counts_as_evidence": False, + "metadata_fallback_counts_as_native_enforcement": False, + "pending_review_counts_as_human_decision": False, + "local_command_runner_counts_as_provider_model": False, + }, + "attestation": { + "real_external_or_human_evidence": True, + "reviewer_or_operator_identity_present": True, + "artifact_refs_reviewed": True, + "privacy_contract_satisfied": True, + "ledger_reviewer_approved": True, + "ledger_reviewer": "Yao ledger reviewer", + "ledger_reviewed_at": "2026-06-14", + }, + } + + +def write_native_telemetry_artifacts(skill_root: Path, *, complete: bool) -> None: + recent_events = ( + [ + { + "command": "chrome-extension", + "event": "skill_activation", + "skill": "yao-meta-skill", + "source": "external", + "version": "1.1.0", + "activation_type": "explicit", + "outcome": "accepted", + "failure_type": "none", + "timestamp": "2026-06-14T10:00:00Z", + } + ] + if complete + else [] + ) + write_json( + skill_root / "reports" / "adoption_drift_report.json", + { + "schema_version": "2.0", + "ok": True, + "privacy_contract": { + "raw_content_allowed": False, + "raw_event_log_packaged": False, + }, + "summary": { + "source_types": {"external": 1 if complete else 0}, + "adoption_sample_count": 1 if complete else 0, + }, + "adoption_by_skill": [ + { + "skill": "yao-meta-skill", + "events": 1 if complete else 0, + "adoption_events": 1 if complete else 0, + "accepted": 1 if complete else 0, + "edited": 0, + "rejected": 0, + "missed": 0, + "adoption_rate": 100.0 if complete else 0, + } + ], + "recent_events": recent_events, + }, + ) + write_json( + skill_root / "reports" / "telemetry_hook_recipes.json", + { + "schema_version": "1.0", + "ok": True, + "privacy_contract": {"raw_content_allowed": False}, + "summary": {"recipe_count": 1, "metadata_only_recipe_count": 1}, + "recipes": [ + { + "id": "chrome-extension", + "source": "external", + "metadata_only": True, + "native_auto_capture": False, + "event": "skill_activation", + "outcome": "accepted", + } + ], + }, + ) + + +def assert_native_telemetry_contract_artifact_validation() -> None: + entry = {"key": "native-client-telemetry", "category": "external"} + skill_root = TMP / "native_telemetry_contract_root" + write_native_telemetry_artifacts(skill_root, complete=True) + valid = validate_payload( + native_telemetry_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-client-telemetry.json", + root=skill_root, + template_expected=False, + ) + assert valid["status"] == "pass", valid + + forged_adoption = json.loads((skill_root / "reports" / "adoption_drift_report.json").read_text(encoding="utf-8")) + forged_adoption["recent_events"][0]["source"] = "manual" + write_json(skill_root / "reports" / "adoption_drift_report.json", forged_adoption) + forged = validate_payload( + native_telemetry_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-client-telemetry.json", + root=skill_root, + template_expected=False, + ) + assert forged["status"] == "fail", forged + assert any("external event rows must cover summary.source_types.external" in error for error in forged["errors"]), ( + forged["errors"] + ) + + write_native_telemetry_artifacts(skill_root, complete=True) + raw_adoption = json.loads((skill_root / "reports" / "adoption_drift_report.json").read_text(encoding="utf-8")) + raw_adoption["recent_events"][0]["raw_prompt"] = "verbatim user prompt must not be accepted" + raw_adoption["recent_events"][0]["messages"] = ["raw transcript-like event payload"] + write_json(skill_root / "reports" / "adoption_drift_report.json", raw_adoption) + raw_adoption_result = validate_payload( + native_telemetry_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-client-telemetry.json", + root=skill_root, + template_expected=False, + ) + assert raw_adoption_result["status"] == "fail", raw_adoption_result + assert any("adoption_drift_report.recent_events[0].raw_prompt" in error for error in raw_adoption_result["errors"]), ( + raw_adoption_result["errors"] + ) + assert any("adoption_drift_report.recent_events[0].messages" in error for error in raw_adoption_result["errors"]), ( + raw_adoption_result["errors"] + ) + + write_native_telemetry_artifacts(skill_root, complete=True) + raw_recipe = json.loads((skill_root / "reports" / "telemetry_hook_recipes.json").read_text(encoding="utf-8")) + raw_recipe["recipes"][0]["message"] = "raw client message must not be accepted" + write_json(skill_root / "reports" / "telemetry_hook_recipes.json", raw_recipe) + raw_recipe_result = validate_payload( + native_telemetry_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-client-telemetry.json", + root=skill_root, + template_expected=False, + ) + assert raw_recipe_result["status"] == "fail", raw_recipe_result + assert any("telemetry_hook_recipes.recipes[0].message" in error for error in raw_recipe_result["errors"]), ( + raw_recipe_result["errors"] + ) + + write_native_telemetry_artifacts(skill_root, complete=False) + invalid = validate_payload( + native_telemetry_submission(skill_root), + entry, + path=skill_root / "evidence" / "world_class" / "submissions" / "native-client-telemetry.json", + root=skill_root, + template_expected=False, + ) + assert invalid["status"] == "fail", invalid + assert any("summary.source_types.external must be >0" in error for error in invalid["errors"]), invalid["errors"] + assert any("summary.adoption_sample_count must be >0" in error for error in invalid["errors"]), invalid["errors"] diff --git a/tests/yao_cli_helpers.py b/tests/yao_cli_helpers.py new file mode 100644 index 0000000..bcf6787 --- /dev/null +++ b/tests/yao_cli_helpers.py @@ -0,0 +1,140 @@ +import json +import os +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +CLI = ROOT / "scripts" / "yao.py" +BENCHMARK_FIXTURE_DIR = ROOT / "tests" / "fixtures" / "github_benchmark_scan" + +sys.path.insert(0, str(ROOT / "scripts")) +import yao as yao_cli_module # noqa: E402 +import yao_cli_adaptation_commands # noqa: E402 +import yao_cli_config # noqa: E402 +import yao_cli_distribution_commands # noqa: E402 +import yao_cli_output_commands # noqa: E402 +import yao_cli_parser # noqa: E402 +import yao_cli_parser_evidence # noqa: E402 +import yao_cli_parser_operator # noqa: E402 +import yao_cli_parser_operations # noqa: E402 +import yao_cli_operator_commands # noqa: E402 +import yao_cli_report_commands # noqa: E402 +import yao_cli_runtime # noqa: E402 +from yao_cli_report_refresh import refresh_root_report_consistency_inputs # noqa: E402 + + +def run(*args: str, input_text: str | None = None) -> dict: + env = dict(os.environ) + env["YAO_CLI_TELEMETRY"] = "0" + env.pop("YAO_CLI_TELEMETRY_EVENTS", None) + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + input=input_text, + env=env, + ) + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stderr": proc.stderr, + } + + +def run_with_env(extra_env: dict[str, str], *args: str) -> dict: + env = dict(os.environ) + env.update(extra_env) + proc = subprocess.run( + [sys.executable, str(CLI), *args], + cwd=ROOT, + capture_output=True, + text=True, + env=env, + ) + payload = json.loads(proc.stdout) + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "payload": payload, + "stderr": proc.stderr, + } + + +def assert_cli_module_contracts() -> None: + assert yao_cli_config.resolve_target("root")["title"] == "Root Description Optimization" + assert yao_cli_config.resolve_promotion_target("root") == "yao-meta-skill" + assert yao_cli_config.infer_archetype("Standardize team review workflow.", "")[0] == "production" + assert yao_cli_config.infer_archetype("Govern release policy.", "")[0] == "governed" + assert "--entry" in yao_cli_config.baseline_compare_args() + assert "scripts/provider_output_eval_runner.py" in yao_cli_config.provider_output_runner_command("openai") + assert "--allow-custom-base-url" in yao_cli_config.provider_output_runner_command("openai", allow_custom_base_url=True) + deepseek_command = yao_cli_config.provider_output_runner_command( + "deepseek", + model="deepseek-v4-flash", + temperature=0.0, + ) + assert "--api-format" in deepseek_command and "chat-completions" in deepseek_command, deepseek_command + assert "--thinking" in deepseek_command and "disabled" in deepseek_command, deepseek_command + assert "--temperature" in deepseek_command and "0.0" in deepseek_command, deepseek_command + assert "DEEPSEEK_API_KEY" in deepseek_command, deepseek_command + assert "api.deepseek.com" not in deepseek_command, deepseek_command + for module in ( + yao_cli_parser, + yao_cli_parser_evidence, + yao_cli_parser_operations, + yao_cli_runtime, + yao_cli_adaptation_commands, + yao_cli_distribution_commands, + yao_cli_output_commands, + yao_cli_operator_commands, + yao_cli_report_commands, + yao_cli_parser_operator, + ): + assert module.SCRIPT_INTERFACE == "internal-module" + assert callable(yao_cli_module.command_review_studio) + + +def assert_help_surface() -> None: + parser_help = yao_cli_module.build_parser().format_help() + expected_help = ( + "quickstart skill-interpretation review-studio python-compat architecture-audit skill-os2-audit skill-os2-coverage " + "install-status localized-doc-sync-check pr-review-report " + "world-class-evidence world-class-ledger world-class-intake world-class-preflight world-class-submission-kit world-class-submission-review world-class-runbook world-class-claim-guard " + "benchmark-reproducibility evidence-consistency output-review-kit output-review-import adapt-scan adapt-propose adapt-apply telemetry-import telemetry-emit telemetry-hooks weekly-curator --record-cli-telemetry" + ).split() + assert all(item in parser_help for item in expected_help), parser_help + + +def assert_created_skill_reports(created: Path) -> None: + expected_reports = [ + "intent-dialogue.md", "intent-confidence.md", "skill-overview.html", "skill-interpretation.html", + "skill-interpretation.json", "review-studio.html", "review-studio.json", "review-viewer.html", + "reference-scan.md", "reference-synthesis.md", "output-risk-profile.md", "artifact-design-profile.md", + "prompt-quality-profile.md", "system-model.md", "skill-ir.json", "compiled_targets.md", + "compiled_targets.json", "iteration-directions.md", "adoption_drift_report.md", + "adoption_drift_report.json", "review_waivers.md", "review_waivers.json", "review_annotations.md", + "review_annotations.json", + ] + assert all((created / "reports" / path).exists() for path in expected_reports), created + + +def assert_creation_report_view(report_view: dict) -> None: + assert report_view["html_report"].endswith("reports/skill-overview.html"), report_view + assert report_view["interpretation_report"].endswith("reports/skill-interpretation.html"), report_view + assert Path(report_view["html_report"]).exists(), report_view + assert Path(report_view["interpretation_report"]).exists(), report_view + assert report_view["review_studio"].endswith("reports/review-studio.html"), report_view + assert Path(report_view["review_studio"]).exists(), report_view + assert "Skill 已创建完成" in report_view["message"], report_view + assert "reports/skill-interpretation.html" in report_view["message"], report_view + assert "Review Studio 2.0" in report_view["message"], report_view + assert "目标编译" in report_view["message"], report_view + assert "reports/compiled_targets.md" in report_view["message"], report_view + assert "概述、指标、原理、触发边界、输入输出、目标编译、质量评估、风险治理、包体资产和升级路线" in report_view["message"], report_view + assert "默认使用中文简体" in report_view["message"], report_view + assert "切换英文版" in report_view["message"], report_view diff --git a/tests/yao_cli_report_refresh.py b/tests/yao_cli_report_refresh.py new file mode 100644 index 0000000..f112f16 --- /dev/null +++ b/tests/yao_cli_report_refresh.py @@ -0,0 +1,16 @@ +from pathlib import Path +from typing import Callable + + +def refresh_root_report_consistency_inputs(run: Callable[..., dict], root: Path) -> None: + """Refresh root reports that evidence-consistency compares by value.""" + + for refresh_args in [ + ("benchmark-reproducibility", str(root), "--generated-at", "2026-06-15"), + ("skill-report", str(root)), + ("skill-interpretation", str(root)), + ("world-class-preflight", str(root), "--generated-at", "2026-06-15"), + ("review-studio", str(root)), + ]: + refresh_result = run(*refresh_args) + assert refresh_result["ok"], refresh_result